mysql_host_data_source_unittest.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. // Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <config.h>
  15. #include <asiolink/io_address.h>
  16. #include <dhcpsrv/tests/test_utils.h>
  17. #include <exceptions/exceptions.h>
  18. #include <dhcpsrv/mysql_connection.h>
  19. #include <dhcpsrv/mysql_host_data_source.h>
  20. #include <dhcpsrv/tests/generic_host_data_source_unittest.h>
  21. #include <dhcpsrv/host_data_source_factory.h>
  22. #include <gtest/gtest.h>
  23. #include <algorithm>
  24. #include <iostream>
  25. #include <sstream>
  26. #include <string>
  27. #include <utility>
  28. using namespace isc;
  29. using namespace isc::asiolink;
  30. using namespace isc::dhcp;
  31. using namespace isc::dhcp::test;
  32. using namespace std;
  33. namespace {
  34. // This holds statements to create and destroy the schema.
  35. #include "schema_mysql_copy.h"
  36. // Connection strings.
  37. // Database: keatest
  38. // Host: localhost
  39. // Username: keatest
  40. // Password: keatest
  41. const char* VALID_TYPE = "type=mysql";
  42. const char* INVALID_TYPE = "type=unknown";
  43. const char* VALID_NAME = "name=keatest";
  44. const char* INVALID_NAME = "name=invalidname";
  45. const char* VALID_HOST = "host=localhost";
  46. const char* INVALID_HOST = "host=invalidhost";
  47. const char* VALID_USER = "user=keatest";
  48. const char* INVALID_USER = "user=invaliduser";
  49. const char* VALID_PASSWORD = "password=keatest";
  50. const char* INVALID_PASSWORD = "password=invalid";
  51. // Given a combination of strings above, produce a connection string.
  52. string connectionString(const char* type, const char* name, const char* host,
  53. const char* user, const char* password) {
  54. const string space = " ";
  55. string result = "";
  56. if (type != NULL) {
  57. result += string(type);
  58. }
  59. if (name != NULL) {
  60. if (! result.empty()) {
  61. result += space;
  62. }
  63. result += string(name);
  64. }
  65. if (host != NULL) {
  66. if (! result.empty()) {
  67. result += space;
  68. }
  69. result += string(host);
  70. }
  71. if (user != NULL) {
  72. if (! result.empty()) {
  73. result += space;
  74. }
  75. result += string(user);
  76. }
  77. if (password != NULL) {
  78. if (! result.empty()) {
  79. result += space;
  80. }
  81. result += string(password);
  82. }
  83. return (result);
  84. }
  85. // Return valid connection string
  86. string
  87. validConnectionString() {
  88. return (connectionString(VALID_TYPE, VALID_NAME, VALID_HOST,
  89. VALID_USER, VALID_PASSWORD));
  90. }
  91. // @brief Clear everything from the database
  92. //
  93. // There is no error checking in this code: if something fails, one of the
  94. // tests will (should) fall over.
  95. void destroySchema() {
  96. MySqlHolder mysql;
  97. // Open database
  98. (void) mysql_real_connect(mysql, "localhost", "keatest",
  99. "keatest", "keatest", 0, NULL, 0);
  100. // Get rid of everything in it.
  101. for (int i = 0; destroy_statement[i] != NULL; ++i) {
  102. (void) mysql_query(mysql, destroy_statement[i]);
  103. }
  104. }
  105. // @brief Create the Schema
  106. //
  107. // Creates all the tables in what is assumed to be an empty database.
  108. //
  109. // There is no error checking in this code: if it fails, one of the tests
  110. // will fall over.
  111. void createSchema() {
  112. MySqlHolder mysql;
  113. // Open database
  114. (void) mysql_real_connect(mysql, "localhost", "keatest",
  115. "keatest", "keatest", 0, NULL, 0);
  116. // Execute creation statements.
  117. for (int i = 0; create_statement[i] != NULL; ++i) {
  118. ASSERT_EQ(0, mysql_query(mysql, create_statement[i]))
  119. << "Failed on statement " << i << ": " << create_statement[i];
  120. }
  121. }
  122. class MySqlHostDataSourceTest : public GenericHostDataSourceTest {
  123. public:
  124. /// @brief Constructor
  125. ///
  126. /// Deletes everything from the database and opens it.
  127. MySqlHostDataSourceTest() {
  128. // Ensure schema is the correct one.
  129. destroySchema();
  130. createSchema();
  131. // Connect to the database
  132. try {
  133. HostDataSourceFactory::create(validConnectionString());
  134. } catch (...) {
  135. std::cerr << "*** ERROR: unable to open database. The test\n"
  136. "*** environment is broken and must be fixed before\n"
  137. "*** the MySQL tests will run correctly.\n"
  138. "*** The reason for the problem is described in the\n"
  139. "*** accompanying exception output.\n";
  140. throw;
  141. }
  142. hdsptr_ = &(HostDataSourceFactory::instance());
  143. }
  144. /// @brief Destructor
  145. ///
  146. /// Rolls back all pending transactions. The deletion of myhdsptr_ will close
  147. /// the database. Then reopen it and delete everything created by the test.
  148. virtual ~MySqlHostDataSourceTest() {
  149. hdsptr_->rollback();
  150. HostDataSourceFactory::destroy();
  151. destroySchema();
  152. }
  153. /// @brief Reopen the database
  154. ///
  155. /// Closes the database and re-open it. Anything committed should be
  156. /// visible.
  157. ///
  158. /// Parameter is ignored for MySQL backend as the v4 and v6 leases share
  159. /// the same database.
  160. void reopen(Universe) {
  161. HostDataSourceFactory::destroy();
  162. HostDataSourceFactory::create(validConnectionString());
  163. hdsptr_ = &(HostDataSourceFactory::instance());
  164. }
  165. };
  166. /// @brief Check that database can be opened
  167. ///
  168. /// This test checks if the MySqlHostDataSource can be instantiated. This happens
  169. /// only if the database can be opened. Note that this is not part of the
  170. /// MySqlLeaseMgr test fixure set. This test checks that the database can be
  171. /// opened: the fixtures assume that and check basic operations.
  172. TEST(MySqlHostDataSource, OpenDatabase) {
  173. // Schema needs to be created for the test to work.
  174. destroySchema();
  175. createSchema();
  176. // Check that lease manager open the database opens correctly and tidy up.
  177. // If it fails, print the error message.
  178. try {
  179. HostDataSourceFactory::create(validConnectionString());
  180. EXPECT_NO_THROW((void) HostDataSourceFactory::instance());
  181. HostDataSourceFactory::destroy();
  182. } catch (const isc::Exception& ex) {
  183. FAIL() << "*** ERROR: unable to open database, reason:\n"
  184. << " " << ex.what() << "\n"
  185. << "*** The test environment is broken and must be fixed\n"
  186. << "*** before the MySQL tests will run correctly.\n";
  187. }
  188. // Check that attempting to get an instance of the lease manager when
  189. // none is set throws an exception.
  190. EXPECT_THROW(HostDataSourceFactory::instance(), NoHostDataSourceManager);
  191. // Check that wrong specification of backend throws an exception.
  192. // (This is really a check on LeaseMgrFactory, but is convenient to
  193. // perform here.)
  194. EXPECT_THROW(HostDataSourceFactory::create(connectionString(
  195. NULL, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
  196. InvalidParameter);
  197. EXPECT_THROW(HostDataSourceFactory::create(connectionString(
  198. INVALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)),
  199. InvalidType);
  200. // Check that invalid login data causes an exception.
  201. EXPECT_THROW(HostDataSourceFactory::create(connectionString(
  202. VALID_TYPE, INVALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)),
  203. DbOpenError);
  204. EXPECT_THROW(HostDataSourceFactory::create(connectionString(
  205. VALID_TYPE, VALID_NAME, INVALID_HOST, VALID_USER, VALID_PASSWORD)),
  206. DbOpenError);
  207. EXPECT_THROW(HostDataSourceFactory::create(connectionString(
  208. VALID_TYPE, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
  209. DbOpenError);
  210. EXPECT_THROW(HostDataSourceFactory::create(connectionString(
  211. VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, INVALID_PASSWORD)),
  212. DbOpenError);
  213. // Check for missing parameters
  214. EXPECT_THROW(HostDataSourceFactory::create(connectionString(
  215. VALID_TYPE, NULL, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
  216. NoDatabaseName);
  217. // Tidy up after the test
  218. destroySchema();
  219. }
  220. /// @brief Check conversion functions
  221. ///
  222. /// The server works using cltt and valid_filetime. In the database, the
  223. /// information is stored as expire_time and valid-lifetime, which are
  224. /// related by
  225. ///
  226. /// expire_time = cltt + valid_lifetime
  227. ///
  228. /// This test checks that the conversion is correct. It does not check that the
  229. /// data is entered into the database correctly, only that the MYSQL_TIME
  230. /// structure used for the entry is correctly set up.
  231. TEST(MySqlConnection, checkTimeConversion) {
  232. const time_t cltt = time(NULL);
  233. const uint32_t valid_lft = 86400; // 1 day
  234. struct tm tm_expire;
  235. MYSQL_TIME mysql_expire;
  236. // Work out what the broken-down time will be for one day
  237. // after the current time.
  238. time_t expire_time = cltt + valid_lft;
  239. (void) localtime_r(&expire_time, &tm_expire);
  240. // Convert to the database time
  241. MySqlConnection::convertToDatabaseTime(cltt, valid_lft, mysql_expire);
  242. // Are the times the same?
  243. EXPECT_EQ(tm_expire.tm_year + 1900, mysql_expire.year);
  244. EXPECT_EQ(tm_expire.tm_mon + 1, mysql_expire.month);
  245. EXPECT_EQ(tm_expire.tm_mday, mysql_expire.day);
  246. EXPECT_EQ(tm_expire.tm_hour, mysql_expire.hour);
  247. EXPECT_EQ(tm_expire.tm_min, mysql_expire.minute);
  248. EXPECT_EQ(tm_expire.tm_sec, mysql_expire.second);
  249. EXPECT_EQ(0, mysql_expire.second_part);
  250. EXPECT_EQ(0, mysql_expire.neg);
  251. // Convert back
  252. time_t converted_cltt = 0;
  253. MySqlConnection::convertFromDatabaseTime(mysql_expire, valid_lft, converted_cltt);
  254. EXPECT_EQ(cltt, converted_cltt);
  255. }
  256. // Test verifies if a host reservation can be added and later retrieved by IPv4
  257. // address. Host uses hw address as identifier.
  258. TEST_F(MySqlHostDataSourceTest, basic4HWAddr) {
  259. testBasic4(true);
  260. }
  261. // Test verifies if a host reservation can be added and later retrieved by IPv4
  262. // address. Host uses client-id (DUID) as identifier.
  263. TEST_F(MySqlHostDataSourceTest, basic4ClientId) {
  264. testBasic4(false);
  265. }
  266. // Test verifies that multiple hosts can be added and later retrieved by their
  267. // reserved IPv4 address. This test uses HW addresses as identifiers.
  268. TEST_F(MySqlHostDataSourceTest, getByIPv4HWaddr) {
  269. testGetByIPv4(true);
  270. }
  271. // Test verifies that multiple hosts can be added and later retrieved by their
  272. // reserved IPv4 address. This test uses client-id (DUID) as identifiers.
  273. TEST_F(MySqlHostDataSourceTest, getByIPv4ClientId) {
  274. testGetByIPv4(false);
  275. }
  276. // Test verifies if a host reservation can be added and later retrieved by
  277. // hardware address.
  278. TEST_F(MySqlHostDataSourceTest, get4ByHWaddr) {
  279. testGet4ByHWAddr();
  280. }
  281. // Test verifies if a host reservation can be added and later retrieved by
  282. // client identifier.
  283. TEST_F(MySqlHostDataSourceTest, get4ByClientId) {
  284. testGet4ByClientId();
  285. }
  286. // Test verifies if hardware address and client identifier are not confused.
  287. TEST_F(MySqlHostDataSourceTest, hwaddrNotClientId1) {
  288. testHWAddrNotClientId();
  289. }
  290. // Test verifies if hardware address and client identifier are not confused.
  291. TEST_F(MySqlHostDataSourceTest, hwaddrNotClientId2) {
  292. testClientIdNotHWAddr();
  293. }
  294. // Test verifies if a host with FQDN hostname can be stored and later retrieved.
  295. TEST_F(MySqlHostDataSourceTest, hostnameFQDN) {
  296. testHostname("foo.example.org", 1);
  297. }
  298. // Test verifies if 100 hosts with unique FQDN hostnames can be stored and later
  299. // retrieved.
  300. TEST_F(MySqlHostDataSourceTest, hostnameFQDN100) {
  301. testHostname("foo.example.org", 100);
  302. }
  303. // Test verifies if a host without any hostname specified can be stored and later
  304. // retrieved.
  305. TEST_F(MySqlHostDataSourceTest, noHostname) {
  306. testHostname("", 1);
  307. }
  308. // Test verifies if the hardware or client-id query can match hardware address.
  309. TEST_F(MySqlHostDataSourceTest, DISABLED_hwaddrOrClientId1) {
  310. /// @todo: The logic behind ::get4(subnet_id, hwaddr, duid) call needs to
  311. /// be discussed.
  312. ///
  313. /// @todo: Add host reservation with hardware address X, try to retrieve
  314. /// host for hardware address X or client identifier Y, verify that the
  315. /// reservation is returned.
  316. }
  317. // Test verifies if the hardware or client-id query can match client-id.
  318. TEST_F(MySqlHostDataSourceTest, DISABLED_hwaddrOrClientId2) {
  319. /// @todo: The logic behind ::get4(subnet_id, hwaddr, duid) call needs to
  320. /// be discussed.
  321. ///
  322. /// @todo: Add host reservation with client identifier Y, try to retrieve
  323. /// host for hardware address X or client identifier Y, verify that the
  324. /// reservation is returned.
  325. }
  326. // Test verifies that host with IPv6 address and DUID can be added and
  327. // later retrieved by IPv6 address.
  328. TEST_F(MySqlHostDataSourceTest, DISABLED_get6AddrWithDuid) {
  329. /// @todo: Uncomment when IPv6 support (4212) is implemented.
  330. testGetByIPv6(BaseHostDataSource::ID_DUID, false);
  331. }
  332. // Test verifies that host with IPv6 address and HWAddr can be added and
  333. // later retrieved by IPv6 address.
  334. TEST_F(MySqlHostDataSourceTest, DISABLED_get6AddrWithHWAddr) {
  335. /// @todo: Uncomment when IPv6 support (4212) is implemented.
  336. testGetByIPv6(BaseHostDataSource::ID_HWADDR, false);
  337. }
  338. // Test verifies that host with IPv6 prefix and DUID can be added and
  339. // later retrieved by IPv6 prefix.
  340. TEST_F(MySqlHostDataSourceTest, DISABLED_get6PrefixWithDuid) {
  341. /// @todo: Uncomment when IPv6 support (4212) is implemented.
  342. testGetByIPv6(BaseHostDataSource::ID_DUID, true);
  343. }
  344. // Test verifies that host with IPv6 prefix and HWAddr can be added and
  345. // later retrieved by IPv6 prefix.
  346. TEST_F(MySqlHostDataSourceTest, DISABLED_get6PrefixWithHWaddr) {
  347. /// @todo: Uncomment when IPv6 support (4212) is implemented.
  348. testGetByIPv6(BaseHostDataSource::ID_HWADDR, true);
  349. }
  350. // Test verifies if a host reservation can be added and later retrieved by
  351. // hardware address.
  352. TEST_F(MySqlHostDataSourceTest, DISABLED_get6ByHWaddr) {
  353. /// @todo: Uncomment when IPv6 support (4212) is implemented.
  354. testGet6ByHWAddr();
  355. }
  356. // Test verifies if a host reservation can be added and later retrieved by
  357. // client identifier.
  358. TEST_F(MySqlHostDataSourceTest, DISABLED_get6ByClientId) {
  359. /// @todo: Uncomment when IPv6 support (4212) is implemented.
  360. testGet6ByClientId();
  361. }
  362. // Test verifies if a host reservation can be stored with both IPv6 address and
  363. // prefix.
  364. TEST_F(MySqlHostDataSourceTest, DISABLED_addr6AndPrefix) {
  365. /// @todo: Implement this test as part of #4212.
  366. /// @todo: Add host reservation with an IPv6 address and IPv6 prefix,
  367. /// retrieve it and verify that both v6 address and prefix are retrieved
  368. /// correctly.
  369. }
  370. // Test verifies if multiple client classes for IPv4 can be stored.
  371. TEST_F(MySqlHostDataSourceTest, DISABLED_multipleClientClasses4) {
  372. /// @todo: Implement this test as part of #4213.
  373. /// Add host reservation with a multiple v4 client-classes, retrieve it and
  374. /// make sure that all client classes are retrieved properly.
  375. }
  376. // Test verifies if multiple client classes for IPv6 can be stored.
  377. TEST_F(MySqlHostDataSourceTest, DISABLED_multipleClientClasses6) {
  378. /// @todo: Implement this test as part of #4213.
  379. /// Add host reservation with a multiple v6 client-classes, retrieve it and
  380. /// make sure that all client classes are retrieved properly.
  381. }
  382. // Test verifies if multiple client classes for both IPv4 and IPv6 can be stored.
  383. TEST_F(MySqlHostDataSourceTest, DISABLED_multipleClientClassesBoth) {
  384. /// @todo: Implement this test as part of #4213..
  385. /// Add host reservation with a multiple v4 and v6 client-classes, retrieve
  386. /// it and make sure that all client classes are retrieved properly. Also,
  387. /// check that the classes are not confused.
  388. }
  389. // Test if the same host can have reservations in different subnets (with the
  390. // same hardware address). The test logic is as follows:
  391. // Insert 10 host reservations for a given physical host (the same
  392. // hardware address), but for different subnets (different subnet-ids).
  393. // Make sure that getAll() returns them all correctly.
  394. TEST_F(MySqlHostDataSourceTest, multipleSubnetsHWAddr) {
  395. testMultipleSubnets(10, true);
  396. }
  397. // Test if the same host can have reservations in different subnets (with the
  398. // same client identifier). The test logic is as follows:
  399. //
  400. // Insert 10 host reservations for a given physical host (the same
  401. // client-identifier), but for different subnets (different subnet-ids).
  402. // Make sure that getAll() returns them correctly.
  403. TEST_F(MySqlHostDataSourceTest, multipleSubnetsClientId) {
  404. testMultipleSubnets(10, false);
  405. }
  406. // Test if host reservations made for different IPv6 subnets are handled correctly.
  407. // The test logic is as follows:
  408. //
  409. // Insert 10 host reservations for different subnets. Make sure that
  410. // get6(subnet-id, ...) calls return correct reservation.
  411. TEST_F(MySqlHostDataSourceTest, subnetId6) {
  412. testSubnetId6(10, BaseHostDataSource::ID_HWADDR);
  413. }
  414. // Test if the duplicate host instances can't be inserted. The test logic is as
  415. // follows: try to add multiple instances of the same host reservation and
  416. // verify that the second and following attempts will throw exceptions.
  417. TEST_F(MySqlHostDataSourceTest, addDuplicate) {
  418. testAddDuplicate();
  419. }
  420. }; // Of anonymous namespace