d2_queue_mgr_unittests.cc 17 KB

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