d2_queue_mgr_unittests.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. // Copyright (C) 2013-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_service.h>
  8. #include <asiolink/interval_timer.h>
  9. #include <d2/d2_queue_mgr.h>
  10. #include <dhcp_ddns/ncr_udp.h>
  11. #include <util/time_utilities.h>
  12. #include <boost/function.hpp>
  13. #include <boost/bind.hpp>
  14. #include <gtest/gtest.h>
  15. #include <gtest/gtest.h>
  16. #include <algorithm>
  17. #include <vector>
  18. using namespace std;
  19. using namespace isc;
  20. using namespace isc::dhcp_ddns;
  21. using namespace isc::d2;
  22. namespace {
  23. /// @brief Defines a list of valid JSON NameChangeRequest test messages.
  24. const char *valid_msgs[] =
  25. {
  26. // Valid Add.
  27. "{"
  28. " \"change-type\" : 0 , "
  29. " \"forward-change\" : true , "
  30. " \"reverse-change\" : false , "
  31. " \"fqdn\" : \"walah.walah.com\" , "
  32. " \"ip-address\" : \"192.168.2.1\" , "
  33. " \"dhcid\" : \"010203040A7F8E3D\" , "
  34. " \"lease-expires-on\" : \"20130121132405\" , "
  35. " \"lease-length\" : 1300 "
  36. "}",
  37. // Valid Remove.
  38. "{"
  39. " \"change-type\" : 1 , "
  40. " \"forward-change\" : true , "
  41. " \"reverse-change\" : false , "
  42. " \"fqdn\" : \"walah.walah.com\" , "
  43. " \"ip-address\" : \"192.168.2.1\" , "
  44. " \"dhcid\" : \"010203040A7F8E3D\" , "
  45. " \"lease-expires-on\" : \"20130121132405\" , "
  46. " \"lease-length\" : 1300 "
  47. "}",
  48. // Valid Add with IPv6 address
  49. "{"
  50. " \"change-type\" : 0 , "
  51. " \"forward-change\" : true , "
  52. " \"reverse-change\" : false , "
  53. " \"fqdn\" : \"walah.walah.com\" , "
  54. " \"ip-address\" : \"fe80::2acf:e9ff:fe12:e56f\" , "
  55. " \"dhcid\" : \"010203040A7F8E3D\" , "
  56. " \"lease-expires-on\" : \"20130121132405\" , "
  57. " \"lease-length\" : 1300 "
  58. "}"
  59. };
  60. static const int VALID_MSG_CNT = sizeof(valid_msgs)/sizeof(char*);
  61. const char* TEST_ADDRESS = "127.0.0.1";
  62. const uint32_t LISTENER_PORT = 5301;
  63. const uint32_t SENDER_PORT = LISTENER_PORT+1;
  64. const long TEST_TIMEOUT = 5 * 1000;
  65. /// @brief Tests that construction with max queue size of zero is not allowed.
  66. TEST(D2QueueMgrBasicTest, construction1) {
  67. asiolink::IOServicePtr io_service;
  68. // Verify that constructing with null IOServicePtr is not allowed.
  69. EXPECT_THROW((D2QueueMgr(io_service)), D2QueueMgrError);
  70. io_service.reset(new isc::asiolink::IOService());
  71. // Verify that constructing with max queue size of zero is not allowed.
  72. EXPECT_THROW(D2QueueMgr(io_service, 0), D2QueueMgrError);
  73. }
  74. /// @brief Tests default construction works.
  75. TEST(D2QueueMgrBasicTest, construction2) {
  76. asiolink::IOServicePtr io_service(new isc::asiolink::IOService());
  77. // Verify that valid constructor works.
  78. D2QueueMgrPtr queue_mgr;
  79. ASSERT_NO_THROW(queue_mgr.reset(new D2QueueMgr(io_service)));
  80. // Verify queue max is defaulted correctly.
  81. EXPECT_EQ(D2QueueMgr::MAX_QUEUE_DEFAULT, queue_mgr->getMaxQueueSize());
  82. }
  83. /// @brief Tests construction with custom queue size works properly
  84. TEST(D2QueueMgrBasicTest, construction3) {
  85. asiolink::IOServicePtr io_service(new isc::asiolink::IOService());
  86. // Verify that custom queue size constructor works.
  87. D2QueueMgrPtr queue_mgr;
  88. ASSERT_NO_THROW(queue_mgr.reset(new D2QueueMgr(io_service, 100)));
  89. // Verify queue max is the custom value.
  90. EXPECT_EQ(100, queue_mgr->getMaxQueueSize());
  91. }
  92. /// @brief Tests QueueMgr's basic queue functions
  93. /// This test verifies that:
  94. /// 1. Following construction queue is empty
  95. /// 2. Attempting to peek at an empty queue is not allowed
  96. /// 3. Attempting to dequeue an empty queue is not allowed
  97. /// 4. Peek returns the first entry on the queue without altering queue content
  98. /// 5. Dequeue removes the first entry on the queue
  99. TEST(D2QueueMgrBasicTest, basicQueue) {
  100. asiolink::IOServicePtr io_service(new isc::asiolink::IOService());
  101. // Construct the manager with max queue size set to number of messages
  102. // we'll use.
  103. D2QueueMgrPtr queue_mgr;
  104. ASSERT_NO_THROW(queue_mgr.reset(new D2QueueMgr(io_service, VALID_MSG_CNT)));
  105. ASSERT_EQ(VALID_MSG_CNT, queue_mgr->getMaxQueueSize());
  106. // Verify queue is empty after construction.
  107. EXPECT_EQ(0, queue_mgr->getQueueSize());
  108. // Verify that peek and dequeue both throw when queue is empty.
  109. EXPECT_THROW(queue_mgr->peek(), D2QueueMgrQueueEmpty);
  110. EXPECT_THROW(queue_mgr->dequeue(), D2QueueMgrQueueEmpty);
  111. // Vector to keep track of the NCRs we que.
  112. std::vector<NameChangeRequestPtr>ref_msgs;
  113. NameChangeRequestPtr ncr;
  114. // Iterate over the list of requests and add each to the queue.
  115. for (int i = 0; i < VALID_MSG_CNT; i++) {
  116. // Create the ncr and add to our reference list.
  117. ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(valid_msgs[i]));
  118. ref_msgs.push_back(ncr);
  119. // Verify that the request can be added to the queue and queue
  120. // size increments accordingly.
  121. EXPECT_NO_THROW(queue_mgr->enqueue(ncr));
  122. EXPECT_EQ(i+1, queue_mgr->getQueueSize());
  123. }
  124. // Loop through and verify that the queue contents match the
  125. // reference list.
  126. for (int i = 0; i < VALID_MSG_CNT; i++) {
  127. // Verify that peek on a non-empty queue returns first entry
  128. // without altering queue content.
  129. EXPECT_NO_THROW(ncr = queue_mgr->peek());
  130. // Verify the peeked entry is the one it should be.
  131. ASSERT_TRUE(ncr);
  132. EXPECT_TRUE (*(ref_msgs[i]) == *ncr);
  133. // Verify that peek did not alter the queue size.
  134. EXPECT_EQ(VALID_MSG_CNT - i, queue_mgr->getQueueSize());
  135. // Verify the dequeueing from non-empty queue works
  136. EXPECT_NO_THROW(queue_mgr->dequeue());
  137. // Verify queue size decrements following dequeue.
  138. EXPECT_EQ(VALID_MSG_CNT - (i + 1), queue_mgr->getQueueSize());
  139. }
  140. // Iterate over the list of requests and add each to the queue.
  141. for (int i = 0; i < VALID_MSG_CNT; i++) {
  142. // Create the ncr and add to our reference list.
  143. ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(valid_msgs[i]));
  144. ref_msgs.push_back(ncr);
  145. EXPECT_NO_THROW(queue_mgr->enqueue(ncr));
  146. }
  147. // Verify queue count is correct.
  148. EXPECT_EQ(VALID_MSG_CNT, queue_mgr->getQueueSize());
  149. // Verify that peekAt returns the correct entry.
  150. EXPECT_NO_THROW(ncr = queue_mgr->peekAt(1));
  151. EXPECT_TRUE (*(ref_msgs[1]) == *ncr);
  152. // Verify that dequeueAt removes the correct entry.
  153. // Removing it, this should shift the queued entries forward by one.
  154. EXPECT_NO_THROW(queue_mgr->dequeueAt(1));
  155. EXPECT_NO_THROW(ncr = queue_mgr->peekAt(1));
  156. EXPECT_TRUE (*(ref_msgs[2]) == *ncr);
  157. // Verify the peekAt and dequeueAt throw when given indexes beyond the end.
  158. EXPECT_THROW(queue_mgr->peekAt(VALID_MSG_CNT + 1), D2QueueMgrInvalidIndex);
  159. EXPECT_THROW(queue_mgr->dequeueAt(VALID_MSG_CNT + 1),
  160. D2QueueMgrInvalidIndex);
  161. }
  162. /// @brief Compares two NameChangeRequests for equality.
  163. bool checkSendVsReceived(NameChangeRequestPtr sent_ncr,
  164. NameChangeRequestPtr received_ncr) {
  165. return ((sent_ncr && received_ncr) &&
  166. (*sent_ncr == *received_ncr));
  167. }
  168. /// @brief Text fixture that allows testing a listener and sender together
  169. /// It derives from both the receive and send handler classes and contains
  170. /// and instance of UDP listener and UDP sender.
  171. class QueueMgrUDPTest : public virtual ::testing::Test,
  172. NameChangeSender::RequestSendHandler {
  173. public:
  174. asiolink::IOServicePtr io_service_;
  175. NameChangeSenderPtr sender_;
  176. isc::asiolink::IntervalTimer test_timer_;
  177. D2QueueMgrPtr queue_mgr_;
  178. NameChangeSender::Result send_result_;
  179. std::vector<NameChangeRequestPtr> sent_ncrs_;
  180. std::vector<NameChangeRequestPtr> received_ncrs_;
  181. QueueMgrUDPTest() : io_service_(new isc::asiolink::IOService()),
  182. test_timer_(*io_service_),
  183. send_result_(NameChangeSender::SUCCESS) {
  184. isc::asiolink::IOAddress addr(TEST_ADDRESS);
  185. // Create our sender instance. Note that reuse_address is true.
  186. sender_.reset(new NameChangeUDPSender(addr, SENDER_PORT,
  187. addr, LISTENER_PORT,
  188. FMT_JSON, *this, 100, true));
  189. // Set the test timeout to break any running tasks if they hang.
  190. test_timer_.setup(boost::bind(&QueueMgrUDPTest::testTimeoutHandler,
  191. this),
  192. TEST_TIMEOUT);
  193. }
  194. void reset_results() {
  195. sent_ncrs_.clear();
  196. received_ncrs_.clear();
  197. }
  198. /// @brief Implements the send completion handler.
  199. virtual void operator ()(const NameChangeSender::Result result,
  200. NameChangeRequestPtr& ncr) {
  201. // save the result and the NCR sent.
  202. send_result_ = result;
  203. sent_ncrs_.push_back(ncr);
  204. }
  205. /// @brief Handler invoked when test timeout is hit.
  206. ///
  207. /// This callback stops all running (hanging) tasks on IO service.
  208. void testTimeoutHandler() {
  209. io_service_->stop();
  210. FAIL() << "Test timeout hit.";
  211. }
  212. };
  213. /// @brief Tests D2QueueMgr's state model.
  214. /// This test verifies that:
  215. /// 1. Upon construction, initial state is NOT_INITTED.
  216. /// 2. Cannot start listening from while state is NOT_INITTED.
  217. /// 3. Successful listener initialization transitions from NOT_INITTED
  218. /// to INITTED.
  219. /// 4. Attempting to initialize the listener from INITTED state is not
  220. /// allowed.
  221. /// 5. Starting listener from INITTED transitions to RUNNING.
  222. /// 6. Stopping the listener transitions from RUNNING to STOPPED.
  223. /// 7. Starting listener from STOPPED transitions to RUNNING.
  224. TEST_F (QueueMgrUDPTest, stateModel) {
  225. // Create the queue manager.
  226. ASSERT_NO_THROW(queue_mgr_.reset(new D2QueueMgr(io_service_,
  227. VALID_MSG_CNT)));
  228. // Verify that the initial state is NOT_INITTED.
  229. EXPECT_EQ(D2QueueMgr::NOT_INITTED, queue_mgr_->getMgrState());
  230. // Verify that trying to listen before when not initialized fails.
  231. EXPECT_THROW(queue_mgr_->startListening(), D2QueueMgrError);
  232. // Verify that initializing the listener moves us to INITTED state.
  233. isc::asiolink::IOAddress addr(TEST_ADDRESS);
  234. EXPECT_NO_THROW(queue_mgr_->initUDPListener(addr, LISTENER_PORT,
  235. FMT_JSON, true));
  236. EXPECT_EQ(D2QueueMgr::INITTED, queue_mgr_->getMgrState());
  237. // Verify that attempting to initialize the listener, from INITTED
  238. // is not allowed.
  239. EXPECT_THROW(queue_mgr_->initUDPListener(addr, LISTENER_PORT,
  240. FMT_JSON, true),
  241. D2QueueMgrError);
  242. // Verify that we can enter the RUNNING from INITTED by starting the
  243. // listener.
  244. EXPECT_NO_THROW(queue_mgr_->startListening());
  245. EXPECT_EQ(D2QueueMgr::RUNNING, queue_mgr_->getMgrState());
  246. // Verify that we can move from RUNNING to STOPPING by stopping the
  247. // listener.
  248. EXPECT_NO_THROW(queue_mgr_->stopListening());
  249. EXPECT_EQ(D2QueueMgr::STOPPING, queue_mgr_->getMgrState());
  250. // Stopping requires IO cancel, which result in a callback.
  251. // So process one event and verify we are STOPPED.
  252. io_service_->run_one();
  253. EXPECT_EQ(D2QueueMgr::STOPPED, queue_mgr_->getMgrState());
  254. // Verify that we can re-enter the RUNNING from STOPPED by starting the
  255. // listener.
  256. EXPECT_NO_THROW(queue_mgr_->startListening());
  257. EXPECT_EQ(D2QueueMgr::RUNNING, queue_mgr_->getMgrState());
  258. // Verify that we cannot remove the listener in the RUNNING state
  259. EXPECT_THROW(queue_mgr_->removeListener(), D2QueueMgrError);
  260. // Stop the listener.
  261. EXPECT_NO_THROW(queue_mgr_->stopListening());
  262. EXPECT_EQ(D2QueueMgr::STOPPING, queue_mgr_->getMgrState());
  263. // Stopping requires IO cancel, which result in a callback.
  264. // So process one event and verify we are STOPPED.
  265. io_service_->run_one();
  266. EXPECT_EQ(D2QueueMgr::STOPPED, queue_mgr_->getMgrState());
  267. // Verify that we can remove the listener in the STOPPED state and
  268. // end up back in NOT_INITTED.
  269. EXPECT_NO_THROW(queue_mgr_->removeListener());
  270. EXPECT_EQ(D2QueueMgr::NOT_INITTED, queue_mgr_->getMgrState());
  271. }
  272. /// @brief Tests D2QueueMgr's ability to manage received requests
  273. /// This test verifies that:
  274. /// 1. Requests can be received, queued, and dequeued
  275. /// 2. Once the queue is full, a subsequent request transitions
  276. /// manager to STOPPED_QUEUE_FULL state.
  277. /// 3. Starting listener returns manager to the RUNNING state.
  278. /// 4. Queue contents are preserved across state transitions.
  279. /// 5. Clearing the queue via the clearQueue() method works.
  280. /// 6. Requests can be received and queued normally after the queue
  281. /// has been emptied.
  282. /// 7. setQueueMax disallows values of 0 or less than current queue size.
  283. TEST_F (QueueMgrUDPTest, liveFeed) {
  284. NameChangeRequestPtr send_ncr;
  285. NameChangeRequestPtr received_ncr;
  286. // Create the queue manager and start listening..
  287. ASSERT_NO_THROW(queue_mgr_.reset(new D2QueueMgr(io_service_,
  288. VALID_MSG_CNT)));
  289. ASSERT_EQ(D2QueueMgr::NOT_INITTED, queue_mgr_->getMgrState());
  290. // Verify that setting max queue size to 0 is not allowed.
  291. EXPECT_THROW(queue_mgr_->setMaxQueueSize(0), D2QueueMgrError);
  292. EXPECT_EQ(VALID_MSG_CNT, queue_mgr_->getMaxQueueSize());
  293. isc::asiolink::IOAddress addr(TEST_ADDRESS);
  294. ASSERT_NO_THROW(queue_mgr_->initUDPListener(addr, LISTENER_PORT,
  295. FMT_JSON, true));
  296. ASSERT_EQ(D2QueueMgr::INITTED, queue_mgr_->getMgrState());
  297. ASSERT_NO_THROW(queue_mgr_->startListening());
  298. ASSERT_EQ(D2QueueMgr::RUNNING, queue_mgr_->getMgrState());
  299. // Place the sender into sending state.
  300. ASSERT_NO_THROW(sender_->startSending(*io_service_));
  301. ASSERT_TRUE(sender_->amSending());
  302. // Iterate over the list of requests sending and receiving
  303. // each one. Verify and dequeue as they arrive.
  304. for (int i = 0; i < VALID_MSG_CNT; i++) {
  305. // Create the ncr and add to our reference list.
  306. ASSERT_NO_THROW(send_ncr = NameChangeRequest::fromJSON(valid_msgs[i]));
  307. ASSERT_NO_THROW(sender_->sendRequest(send_ncr));
  308. // running two should do the send then the receive
  309. io_service_->run_one();
  310. io_service_->run_one();
  311. // Verify that the request can be added to the queue and queue
  312. // size increments accordingly.
  313. EXPECT_EQ(1, queue_mgr_->getQueueSize());
  314. // Verify that peek shows the NCR we just sent
  315. EXPECT_NO_THROW(received_ncr = queue_mgr_->peek());
  316. EXPECT_TRUE(checkSendVsReceived(send_ncr, received_ncr));
  317. // Verify that we and dequeue the request.
  318. EXPECT_NO_THROW(queue_mgr_->dequeue());
  319. EXPECT_EQ(0, queue_mgr_->getQueueSize());
  320. }
  321. // Iterate over the list of requests, sending and receiving
  322. // each one. Allow them to accumulate in the queue.
  323. for (int i = 0; i < VALID_MSG_CNT; i++) {
  324. // Create the ncr and add to our reference list.
  325. ASSERT_NO_THROW(send_ncr = NameChangeRequest::fromJSON(valid_msgs[i]));
  326. ASSERT_NO_THROW(sender_->sendRequest(send_ncr));
  327. // running two should do the send then the receive
  328. EXPECT_NO_THROW(io_service_->run_one());
  329. EXPECT_NO_THROW(io_service_->run_one());
  330. EXPECT_EQ(i+1, queue_mgr_->getQueueSize());
  331. }
  332. // Verify that the queue is at max capacity.
  333. EXPECT_EQ(queue_mgr_->getMaxQueueSize(), queue_mgr_->getQueueSize());
  334. // Send another. The send should succeed.
  335. ASSERT_NO_THROW(sender_->sendRequest(send_ncr));
  336. EXPECT_NO_THROW(io_service_->run_one());
  337. // Now execute the receive which should not throw but should move us
  338. // to STOPPED_QUEUE_FULL state.
  339. EXPECT_NO_THROW(io_service_->run_one());
  340. EXPECT_EQ(D2QueueMgr::STOPPED_QUEUE_FULL, queue_mgr_->getMgrState());
  341. // Verify queue size did not increase beyond max.
  342. EXPECT_EQ(VALID_MSG_CNT, queue_mgr_->getQueueSize());
  343. // Verify that setting max queue size to a value less than current size of
  344. // the queue is not allowed.
  345. EXPECT_THROW(queue_mgr_->setMaxQueueSize(VALID_MSG_CNT-1), D2QueueMgrError);
  346. EXPECT_EQ(VALID_MSG_CNT, queue_mgr_->getQueueSize());
  347. // Verify that we can re-enter RUNNING from STOPPED_QUEUE_FULL.
  348. EXPECT_NO_THROW(queue_mgr_->startListening());
  349. EXPECT_EQ(D2QueueMgr::RUNNING, queue_mgr_->getMgrState());
  350. // Verify that the queue contents were preserved.
  351. EXPECT_EQ(queue_mgr_->getMaxQueueSize(), queue_mgr_->getQueueSize());
  352. // Verify that clearQueue works.
  353. EXPECT_NO_THROW(queue_mgr_->clearQueue());
  354. EXPECT_EQ(0, queue_mgr_->getQueueSize());
  355. // Verify that we can again receive requests.
  356. // Send should be fine.
  357. ASSERT_NO_THROW(sender_->sendRequest(send_ncr));
  358. EXPECT_NO_THROW(io_service_->run_one());
  359. // Receive should succeed.
  360. EXPECT_NO_THROW(io_service_->run_one());
  361. EXPECT_EQ(1, queue_mgr_->getQueueSize());
  362. }
  363. } // end of anonymous namespace