d2_queue_mgr_unittests.cc 17 KB

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