d2_update_mgr_unittests.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. // Copyright (C) 2013 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 <d2/d2_asio.h>
  15. #include <d2/d2_update_mgr.h>
  16. #include <util/time_utilities.h>
  17. #include <d_test_stubs.h>
  18. #include <boost/function.hpp>
  19. #include <boost/bind.hpp>
  20. #include <gtest/gtest.h>
  21. #include <gtest/gtest.h>
  22. #include <algorithm>
  23. #include <vector>
  24. using namespace std;
  25. using namespace isc;
  26. using namespace isc::dhcp_ddns;
  27. using namespace isc::d2;
  28. namespace {
  29. /// @brief Wrapper class for D2UpdateMgr to provide acces non-public methods.
  30. ///
  31. /// This class faciliates testing by making non-public methods accessible so
  32. /// they can be invoked directly in test routines.
  33. class D2UpdateMgrWrapper : public D2UpdateMgr {
  34. public:
  35. /// @brief Constructor
  36. ///
  37. /// Parameters match those needed by D2UpdateMgr.
  38. D2UpdateMgrWrapper(D2QueueMgrPtr& queue_mgr, D2CfgMgrPtr& cfg_mgr,
  39. IOServicePtr& io_service,
  40. const size_t max_transactions = MAX_TRANSACTIONS_DEFAULT)
  41. : D2UpdateMgr(queue_mgr, cfg_mgr, io_service, max_transactions) {
  42. }
  43. /// @brief Destructor
  44. virtual ~D2UpdateMgrWrapper() {
  45. }
  46. // Expose the protected methods to be tested.
  47. using D2UpdateMgr::checkFinishedTransactions;
  48. using D2UpdateMgr::pickNextJob;
  49. using D2UpdateMgr::makeTransaction;
  50. };
  51. /// @brief Defines a pointer to a D2UpdateMgr instance.
  52. typedef boost::shared_ptr<D2UpdateMgrWrapper> D2UpdateMgrWrapperPtr;
  53. /// @brief Test fixture for testing D2UpdateMgr.
  54. ///
  55. /// Note this class uses D2UpdateMgrWrapper class to exercise non-public
  56. /// aspects of D2UpdateMgr. D2UpdateMgr depends on both D2QueueMgr and
  57. /// D2CfgMgr. This fixture provides an instance of each, plus a canned,
  58. /// valid DHCP_DDNS configuration sufficient to test D2UpdateMgr's basic
  59. /// functions.
  60. class D2UpdateMgrTest : public ConfigParseTest {
  61. public:
  62. IOServicePtr io_service_;
  63. D2QueueMgrPtr queue_mgr_;
  64. D2CfgMgrPtr cfg_mgr_;
  65. //D2UpdateMgrPtr update_mgr_;
  66. D2UpdateMgrWrapperPtr update_mgr_;
  67. std::vector<NameChangeRequestPtr> canned_ncrs_;
  68. size_t canned_count_;
  69. D2UpdateMgrTest() {
  70. io_service_.reset(new isc::asiolink::IOService());
  71. queue_mgr_.reset(new D2QueueMgr(io_service_));
  72. cfg_mgr_.reset(new D2CfgMgr());
  73. update_mgr_.reset(new D2UpdateMgrWrapper(queue_mgr_, cfg_mgr_,
  74. io_service_));
  75. makeCannedNcrs();
  76. makeCannedConfig();
  77. }
  78. ~D2UpdateMgrTest() {
  79. }
  80. /// @brief Creates a list of valid NameChangeRequest.
  81. ///
  82. /// This method builds a list of NameChangeRequests from a single
  83. /// JSON string request. Each request is assigned a unique DHCID.
  84. void makeCannedNcrs() {
  85. const char* msg_str =
  86. "{"
  87. " \"change_type\" : 0 , "
  88. " \"forward_change\" : true , "
  89. " \"reverse_change\" : false , "
  90. " \"fqdn\" : \"walah.walah.org.\" , "
  91. " \"ip_address\" : \"192.168.2.1\" , "
  92. " \"dhcid\" : \"0102030405060708\" , "
  93. " \"lease_expires_on\" : \"20130121132405\" , "
  94. " \"lease_length\" : 1300 "
  95. "}";
  96. const char* dhcids[] = { "111111", "222222", "333333", "444444"};
  97. canned_count_ = 4;
  98. for (int i = 0; i < canned_count_; i++) {
  99. dhcp_ddns::NameChangeRequestPtr ncr = NameChangeRequest::
  100. fromJSON(msg_str);
  101. ncr->setDhcid(dhcids[i]);
  102. canned_ncrs_.push_back(ncr);
  103. }
  104. }
  105. /// @brief Seeds configuration manager with a valid DHCP_DDNS configuration.
  106. void makeCannedConfig() {
  107. std::string canned_config_ =
  108. "{ "
  109. "\"interface\" : \"eth1\" , "
  110. "\"ip_address\" : \"192.168.1.33\" , "
  111. "\"port\" : 88 , "
  112. "\"tsig_keys\": [] ,"
  113. "\"forward_ddns\" : {"
  114. "\"ddns_domains\": [ "
  115. "{ \"name\": \"two.three.org.\" , "
  116. " \"dns_servers\" : [ "
  117. " { \"ip_address\": \"127.0.0.1\" } "
  118. " ] },"
  119. "{ \"name\": \"org.\" , "
  120. " \"dns_servers\" : [ "
  121. " { \"ip_address\": \"127.0.0.1\" } "
  122. " ] }, "
  123. "] }, "
  124. "\"reverse_ddns\" : { "
  125. "\"ddns_domains\": [ "
  126. "{ \"name\": \"1.168.192.in-addr.arpa.\" , "
  127. " \"dns_servers\" : [ "
  128. " { \"ip_address\": \"127.0.0.1\" } "
  129. " ] }, "
  130. "{ \"name\": \"2.0.3.0.8.B.D.0.1.0.0.2.ip6.arpa.\" , "
  131. " \"dns_servers\" : [ "
  132. " { \"ip_address\": \"127.0.0.1\" } "
  133. " ] } "
  134. "] } }";
  135. // If this configuration fails to parse most tests will fail.
  136. ASSERT_TRUE(fromJSON(canned_config_));
  137. answer_ = cfg_mgr_->parseConfig(config_set_);
  138. ASSERT_TRUE(checkAnswer(0));
  139. }
  140. };
  141. /// @brief Tests the D2UpdateMgr construction.
  142. /// This test verifies that:
  143. /// 1. Construction with invalid queue manager is not allowed
  144. /// 2. Construction with invalid configuration manager is not allowed
  145. /// 3. Construction with max transactions of zero is not allowed
  146. /// 4. Default construction works and max transactions is defaulted properly
  147. /// 5. Construction with custom max transactions works properly
  148. TEST(D2UpdateMgr, construction) {
  149. IOServicePtr io_service(new isc::asiolink::IOService());
  150. D2QueueMgrPtr queue_mgr;
  151. D2CfgMgrPtr cfg_mgr;
  152. D2UpdateMgrPtr update_mgr;
  153. // Verify that constrctor fails if given an invalid queue manager.
  154. ASSERT_NO_THROW(cfg_mgr.reset(new D2CfgMgr()));
  155. EXPECT_THROW(D2UpdateMgr(queue_mgr, cfg_mgr, io_service),
  156. D2UpdateMgrError);
  157. // Verify that constrctor fails if given an invalid config manager.
  158. ASSERT_NO_THROW(queue_mgr.reset(new D2QueueMgr(io_service)));
  159. ASSERT_NO_THROW(cfg_mgr.reset());
  160. EXPECT_THROW(D2UpdateMgr(queue_mgr, cfg_mgr, io_service),
  161. D2UpdateMgrError);
  162. ASSERT_NO_THROW(cfg_mgr.reset(new D2CfgMgr()));
  163. // Verify that constructor fails with invalid io_service.
  164. io_service.reset();
  165. EXPECT_THROW(D2UpdateMgr(queue_mgr, cfg_mgr, io_service),
  166. D2UpdateMgrError);
  167. io_service.reset(new isc::asiolink::IOService());
  168. // Verify that max transactions cannot be zero.
  169. EXPECT_THROW(D2UpdateMgr(queue_mgr, cfg_mgr, io_service, 0),
  170. D2UpdateMgrError);
  171. // Verify that given valid values, constructor works.
  172. ASSERT_NO_THROW(update_mgr.reset(new D2UpdateMgr(queue_mgr, cfg_mgr,
  173. io_service)));
  174. // Verify that max transactions defaults properly.
  175. EXPECT_EQ(D2UpdateMgr::MAX_TRANSACTIONS_DEFAULT,
  176. update_mgr->getMaxTransactions());
  177. // Verify that constructor permits custom max transactions.
  178. ASSERT_NO_THROW(update_mgr.reset(new D2UpdateMgr(queue_mgr, cfg_mgr,
  179. io_service, 100)));
  180. // Verify that max transactions is correct.
  181. EXPECT_EQ(100, update_mgr->getMaxTransactions());
  182. }
  183. /// @brief Tests the D2UpdateManager's transaction list services
  184. /// This test verifies that:
  185. /// 1. A transaction can be added to the list.
  186. /// 2. Finding a transaction in the list by key works correctly.
  187. /// 3. Looking for a non-existant transaction works properly.
  188. /// 4. Attempting to add a transaction for a DHCID already in the list fails.
  189. /// 5. Removing a transaction by key works properly.
  190. /// 6. Attempting to remove an non-existant transaction does no harm.
  191. TEST_F(D2UpdateMgrTest, transactionList) {
  192. // Grab a canned request for test purposes.
  193. NameChangeRequestPtr& ncr = canned_ncrs_[0];
  194. TransactionList::iterator pos;
  195. // Verify that we can add a transaction.
  196. EXPECT_NO_THROW(update_mgr_->makeTransaction(ncr));
  197. EXPECT_EQ(1, update_mgr_->getTransactionCount());
  198. // Verify that we can find a transaction by key.
  199. EXPECT_NO_THROW(pos = update_mgr_->findTransaction(ncr->getDhcid()));
  200. EXPECT_TRUE(pos != update_mgr_->transactionListEnd());
  201. // Verify that convenience method has same result.
  202. EXPECT_TRUE(update_mgr_->hasTransaction(ncr->getDhcid()));
  203. // Verify that we will not find a transaction that isn't there.
  204. dhcp_ddns::D2Dhcid bogus_id("FFFF");
  205. EXPECT_NO_THROW(pos = update_mgr_->findTransaction(bogus_id));
  206. EXPECT_TRUE(pos == update_mgr_->transactionListEnd());
  207. // Verify that convenience method has same result.
  208. EXPECT_FALSE(update_mgr_->hasTransaction(bogus_id));
  209. // Verify that adding a transaction for the same key fails.
  210. EXPECT_THROW(update_mgr_->makeTransaction(ncr), D2UpdateMgrError);
  211. EXPECT_EQ(1, update_mgr_->getTransactionCount());
  212. // Verify the we can remove a transaction by key.
  213. EXPECT_NO_THROW(update_mgr_->removeTransaction(ncr->getDhcid()));
  214. EXPECT_EQ(0, update_mgr_->getTransactionCount());
  215. // Verify the we can try to remove a non-existant transaction without harm.
  216. EXPECT_NO_THROW(update_mgr_->removeTransaction(ncr->getDhcid()));
  217. }
  218. /// @brief Tests D2UpdateManager's checkFinishedTransactions method.
  219. /// This test verifies that:
  220. /// 1. Completed transactions are removed from the transaction list.
  221. /// 2. Failed transactions are removed from the transaction list.
  222. /// @todo This test will need to expand if and when checkFinishedTransactions
  223. /// method expands to do more than remove them from the list.
  224. TEST_F(D2UpdateMgrTest, checkFinishedTransaction) {
  225. // Ensure we have at least 4 canned requests with which to work.
  226. ASSERT_TRUE(canned_count_ >= 4);
  227. // Create a transaction for each canned request.
  228. for (int i = 0; i < canned_count_; i++) {
  229. EXPECT_NO_THROW(update_mgr_->makeTransaction(canned_ncrs_[i]));
  230. }
  231. // Verfiy we have that the transaçtion count is correct.
  232. EXPECT_EQ(canned_count_, update_mgr_->getTransactionCount());
  233. // Set two of the transactions to finished states.
  234. (canned_ncrs_[1])->setStatus(dhcp_ddns::ST_COMPLETED);
  235. (canned_ncrs_[3])->setStatus(dhcp_ddns::ST_FAILED);
  236. // Verify that invoking checkFinishedTransactions does not throw.
  237. EXPECT_NO_THROW(update_mgr_->checkFinishedTransactions());
  238. // Verify that the list of transactions has decreased by two.
  239. EXPECT_EQ(canned_count_ - 2, update_mgr_->getTransactionCount());
  240. // Vefity that the transaction list is correct.
  241. EXPECT_TRUE(update_mgr_->hasTransaction(canned_ncrs_[0]->getDhcid()));
  242. EXPECT_FALSE(update_mgr_->hasTransaction(canned_ncrs_[1]->getDhcid()));
  243. EXPECT_TRUE(update_mgr_->hasTransaction(canned_ncrs_[2]->getDhcid()));
  244. EXPECT_FALSE(update_mgr_->hasTransaction(canned_ncrs_[3]->getDhcid()));
  245. }
  246. /// @brief Tests D2UpdateManager's pickNextJob method.
  247. /// This test verifies that:
  248. /// 1. pickNextJob will select and make transactions from NCR queue.
  249. /// 2. Requests are removed from the queue once selected
  250. /// 3. Requests for DHCIDs with transactions already in progress are not
  251. /// selected.
  252. /// 4. Requests with no matching servers are removed from the queue and
  253. /// discarded.
  254. TEST_F(D2UpdateMgrTest, pickNextJob) {
  255. // Ensure we have at least 4 canned requests with which to work.
  256. ASSERT_TRUE(canned_count_ >= 4);
  257. // Put each transaction on the queue.
  258. for (int i = 0; i < canned_count_; i++) {
  259. ASSERT_NO_THROW(queue_mgr_->enqueue(canned_ncrs_[i]));
  260. }
  261. // Invoke pickNextJob canned_count_ times which should create a
  262. // transaction for each canned ncr.
  263. for (int i = 0; i < canned_count_; i++) {
  264. EXPECT_NO_THROW(update_mgr_->pickNextJob());
  265. EXPECT_EQ(i + 1, update_mgr_->getTransactionCount());
  266. EXPECT_TRUE(update_mgr_->hasTransaction(canned_ncrs_[i]->getDhcid()));
  267. }
  268. // Verify that the queue has been drained.
  269. EXPECT_EQ(0, update_mgr_->getQueueCount());
  270. // Now verify that a subsequent request for a DCHID for which a
  271. // transaction is in progress, is not dequeued.
  272. // First add the "subsequent" request.
  273. dhcp_ddns::NameChangeRequestPtr
  274. subsequent_ncr(new dhcp_ddns::NameChangeRequest(*(canned_ncrs_[2])));
  275. EXPECT_NO_THROW(queue_mgr_->enqueue(subsequent_ncr));
  276. EXPECT_EQ(1, update_mgr_->getQueueCount());
  277. // Verify that invoking pickNextJob:
  278. // 1. does not throw
  279. // 2. does not make a new transaction
  280. // 3. does not dequeu the entry
  281. EXPECT_NO_THROW(update_mgr_->pickNextJob());
  282. EXPECT_EQ(canned_count_, update_mgr_->getTransactionCount());
  283. EXPECT_EQ(1, update_mgr_->getQueueCount());
  284. // Clear out the queue and transaction list.
  285. queue_mgr_->clearQueue();
  286. update_mgr_->clearTransactionList();
  287. // Make a forward change NCR with an FQDN that has no forward match.
  288. dhcp_ddns::NameChangeRequestPtr
  289. bogus_ncr(new dhcp_ddns::NameChangeRequest(*(canned_ncrs_[0])));
  290. bogus_ncr->setForwardChange(true);
  291. bogus_ncr->setReverseChange(false);
  292. bogus_ncr->setFqdn("bogus.forward.domain.com");
  293. // Put it on the queue up
  294. ASSERT_NO_THROW(queue_mgr_->enqueue(bogus_ncr));
  295. // Verify that invoking pickNextJob:
  296. // 1. does not throw
  297. // 2. does not make a new transaction
  298. // 3. does dequeue the entry
  299. EXPECT_NO_THROW(update_mgr_->pickNextJob());
  300. EXPECT_EQ(0, update_mgr_->getTransactionCount());
  301. EXPECT_EQ(0, update_mgr_->getQueueCount());
  302. // Make a reverse change NCR with an FQDN that has no reverse match.
  303. bogus_ncr.reset(new dhcp_ddns::NameChangeRequest(*(canned_ncrs_[0])));
  304. bogus_ncr->setForwardChange(false);
  305. bogus_ncr->setReverseChange(true);
  306. bogus_ncr->setIpAddress("77.77.77.77");
  307. // Verify that invoking pickNextJob:
  308. // 1. does not throw
  309. // 2. does not make a new transaction
  310. // 3. does dequeue the entry
  311. EXPECT_NO_THROW(update_mgr_->pickNextJob());
  312. EXPECT_EQ(0, update_mgr_->getTransactionCount());
  313. EXPECT_EQ(0, update_mgr_->getQueueCount());
  314. }
  315. /// @brief Tests D2UpdateManager's sweep method.
  316. /// Since sweep is primarly a wrapper around chechFinishedTransactions and
  317. /// pickNextJob, along with checks on maximum transaction limits, it mostly
  318. /// verifies that these three pieces work togther to move process jobs.
  319. /// Most of what is tested here is tested above.
  320. TEST_F(D2UpdateMgrTest, sweep) {
  321. // Ensure we have at least 4 canned requests with which to work.
  322. ASSERT_TRUE(canned_count_ >= 4);
  323. // Set max transactions to same as current transaction count.
  324. EXPECT_NO_THROW(update_mgr_->setMaxTransactions(canned_count_));
  325. EXPECT_EQ(canned_count_, update_mgr_->getMaxTransactions());
  326. // Put each transaction on the queue.
  327. for (int i = 0; i < canned_count_; i++) {
  328. EXPECT_NO_THROW(queue_mgr_->enqueue(canned_ncrs_[i]));
  329. }
  330. // Invoke sweep canned_count_ times which should create a
  331. // transaction for each canned ncr.
  332. for (int i = 0; i < canned_count_; i++) {
  333. EXPECT_NO_THROW(update_mgr_->sweep());
  334. EXPECT_EQ(i + 1, update_mgr_->getTransactionCount());
  335. EXPECT_TRUE(update_mgr_->hasTransaction(canned_ncrs_[i]->getDhcid()));
  336. }
  337. // Verify that the queue has been drained.
  338. EXPECT_EQ(0, update_mgr_->getQueueCount());
  339. // Verify max transactions can't be less than current transaction count.
  340. EXPECT_THROW(update_mgr_->setMaxTransactions(1), D2UpdateMgrError);
  341. // Queue up a request for a DCHID which has a transaction in progress.
  342. dhcp_ddns::NameChangeRequestPtr
  343. subsequent_ncr(new dhcp_ddns::NameChangeRequest(*(canned_ncrs_[2])));
  344. EXPECT_NO_THROW(queue_mgr_->enqueue(subsequent_ncr));
  345. EXPECT_EQ(1, update_mgr_->getQueueCount());
  346. // Verify that invoking sweep, does not dequeue the job nor make a
  347. // transaction for it.
  348. EXPECT_NO_THROW(update_mgr_->sweep());
  349. EXPECT_EQ(canned_count_, update_mgr_->getTransactionCount());
  350. EXPECT_EQ(1, update_mgr_->getQueueCount());
  351. // Mark the transaction complete.
  352. (canned_ncrs_[2])->setStatus(dhcp_ddns::ST_COMPLETED);
  353. // Verify that invoking sweep, cleans up the completed transaction,
  354. // dequeues the queued job and adds its transaction to the list.
  355. EXPECT_NO_THROW(update_mgr_->sweep());
  356. EXPECT_EQ(canned_count_, update_mgr_->getTransactionCount());
  357. EXPECT_EQ(0, update_mgr_->getQueueCount());
  358. // Queue up a request from a new DHCID.
  359. dhcp_ddns::NameChangeRequestPtr
  360. another_ncr(new dhcp_ddns::NameChangeRequest(*(canned_ncrs_[0])));
  361. another_ncr->setDhcid("AABBCCDDEEFF");
  362. EXPECT_NO_THROW(queue_mgr_->enqueue(another_ncr));
  363. EXPECT_EQ(1, update_mgr_->getQueueCount());
  364. // Verify that sweep does not dequeue the new request as we are at
  365. // transaction count.
  366. EXPECT_NO_THROW(update_mgr_->sweep());
  367. EXPECT_EQ(canned_count_, update_mgr_->getTransactionCount());
  368. EXPECT_EQ(1, update_mgr_->getQueueCount());
  369. // Set max transactions to same as current transaction count.
  370. EXPECT_NO_THROW(update_mgr_->setMaxTransactions(canned_count_ + 1));
  371. // Verify that invoking sweep, dequeues the request and creates
  372. // a transaction for it.
  373. EXPECT_NO_THROW(update_mgr_->sweep());
  374. EXPECT_EQ(canned_count_ + 1, update_mgr_->getTransactionCount());
  375. EXPECT_EQ(0, update_mgr_->getQueueCount());
  376. // Verify that clearing transaction list works.
  377. EXPECT_NO_THROW(update_mgr_->clearTransactionList());
  378. EXPECT_EQ(0, update_mgr_->getTransactionCount());
  379. }
  380. }