stats_mgr_unittest.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. // Copyright (C) 2012 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 <boost/shared_ptr.hpp>
  15. #include <exceptions/exceptions.h>
  16. #include <dhcp/dhcp4.h>
  17. #include <dhcp/dhcp6.h>
  18. #include <dhcp/pkt4.h>
  19. #include <dhcp/pkt6.h>
  20. #include <gtest/gtest.h>
  21. #include "../stats_mgr.h"
  22. using namespace std;
  23. using namespace isc;
  24. using namespace isc::dhcp;
  25. using namespace isc::perfdhcp;
  26. namespace {
  27. typedef StatsMgr<dhcp::Pkt4> StatsMgr4;
  28. typedef StatsMgr<dhcp::Pkt6> StatsMgr6;
  29. const uint32_t common_transid = 123;
  30. class StatsMgrTest : public ::testing::Test {
  31. public:
  32. StatsMgrTest() {
  33. }
  34. /// \brief Create DHCPv4 packet.
  35. ///
  36. /// Method creates DHCPv4 packet and updates its timestamp.
  37. ///
  38. /// \param msg_type DHCPv4 message type.
  39. /// \param transid transaction id for the packet.
  40. /// \return DHCPv4 packet.
  41. Pkt4* createPacket4(const uint8_t msg_type,
  42. const uint32_t transid) {
  43. Pkt4* pkt = new Pkt4(msg_type, transid);
  44. // Packet timestamp is normally updated by interface
  45. // manager on packets reception or send. Unit tests
  46. // do not use interface manager so we need to do it
  47. // ourselfs.
  48. pkt->updateTimestamp();
  49. return pkt;
  50. }
  51. /// \brief Create DHCPv6 packet.
  52. ///
  53. /// Method creates DHCPv6 packet and updates its timestamp.
  54. ///
  55. /// \param msg_type DHCPv6 message type.
  56. /// \param transid transaction id.
  57. /// \return DHCPv6 packet.
  58. Pkt6* createPacket6(const uint8_t msg_type,
  59. const uint32_t transid) {
  60. Pkt6* pkt = new Pkt6(msg_type, transid);
  61. // Packet timestamp is normally updated by interface
  62. // manager on packets reception or send. Unit tests
  63. // do not use interface manager so we need to do it
  64. // ourselfs.
  65. pkt->updateTimestamp();
  66. return pkt;
  67. }
  68. /// \brief Pass multiple DHCPv6 packets to Statistics Manager.
  69. ///
  70. /// Method simulates sending or receiving multiple DHCPv6 packets.
  71. ///
  72. /// \param stats_mgr Statistics Manager instance to be used.
  73. /// \param xchg_type packet exchange types.
  74. /// \param packet_type DHCPv6 packet type.
  75. /// \param num_packets packets to be passed to Statistics Manager.
  76. /// \param receive simulated packets are received (if true)
  77. /// or sent (if false)
  78. void passMultiplePackets6(const boost::shared_ptr<StatsMgr6> stats_mgr,
  79. const StatsMgr6::ExchangeType xchg_type,
  80. const uint8_t packet_type,
  81. const int num_packets,
  82. const bool receive = false) {
  83. for (int i = 0; i < num_packets; ++i) {
  84. boost::shared_ptr<Pkt6>
  85. packet(createPacket6(packet_type, i));
  86. if (receive) {
  87. ASSERT_NO_THROW(
  88. stats_mgr->passRcvdPacket(xchg_type, packet);
  89. );
  90. } else {
  91. ASSERT_NO_THROW(
  92. stats_mgr->passSentPacket(xchg_type, packet)
  93. );
  94. }
  95. }
  96. }
  97. /// \brief Simulate DHCPv4 DISCOVER-OFFER with delay.
  98. ///
  99. /// Method simulates DHCPv4 DISCOVER-OFFER exchange. The OFFER packet
  100. /// creation is delayed by the specified number of seconds. This imposes
  101. /// different packet timestamps and affects delay counters in Statistics
  102. /// Manager.
  103. ///
  104. /// \param stats_mgr Statistics Manager instance.
  105. /// \param delay delay in seconds between DISCOVER and OFFER packets.
  106. void passDOPacketsWithDelay(const boost::shared_ptr<StatsMgr4> stats_mgr,
  107. unsigned int delay,
  108. uint32_t transid) {
  109. boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER,
  110. transid));
  111. ASSERT_NO_THROW(
  112. stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet)
  113. );
  114. // There is way to differentiate timstamps of two packets other than
  115. // sleep for before we create another packet. Packet is using current
  116. // time to update its timestamp.
  117. // Sleeping for X seconds will guarantee that delay between packets
  118. // will be greater than 1 second. Note that posix time value is
  119. // transformed to double value and it makes it hard to determine
  120. // actual value to expect.
  121. std::cout << "Sleeping for " << delay << "s to test packet delays"
  122. << std::endl;
  123. sleep(delay);
  124. boost::shared_ptr<Pkt4> rcvd_packet(createPacket4(DHCPOFFER,
  125. transid));
  126. ASSERT_NO_THROW(
  127. stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet);
  128. );
  129. // Calculate period between packets.
  130. boost::posix_time::ptime sent_time = sent_packet->getTimestamp();
  131. boost::posix_time::ptime rcvd_time = rcvd_packet->getTimestamp();
  132. ASSERT_FALSE(sent_time.is_not_a_date_time());
  133. ASSERT_FALSE(rcvd_time.is_not_a_date_time());
  134. }
  135. };
  136. TEST_F(StatsMgrTest, Constructor) {
  137. boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
  138. stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
  139. EXPECT_DOUBLE_EQ(
  140. std::numeric_limits<double>::max(),
  141. stats_mgr->getMinDelay(StatsMgr4::XCHG_DO)
  142. );
  143. EXPECT_DOUBLE_EQ(0, stats_mgr->getMaxDelay(StatsMgr4::XCHG_DO));
  144. EXPECT_EQ(0, stats_mgr->getOrphans(StatsMgr4::XCHG_DO));
  145. EXPECT_EQ(0, stats_mgr->getOrderedLookups(StatsMgr4::XCHG_DO));
  146. EXPECT_EQ(0, stats_mgr->getUnorderedLookups(StatsMgr4::XCHG_DO));
  147. EXPECT_EQ(0, stats_mgr->getSentPacketsNum(StatsMgr4::XCHG_DO));
  148. EXPECT_EQ(0, stats_mgr->getRcvdPacketsNum(StatsMgr4::XCHG_DO));
  149. EXPECT_EQ(0, stats_mgr->getCollectedNum(StatsMgr4::XCHG_DO));
  150. EXPECT_THROW(stats_mgr->getAvgDelay(StatsMgr4::XCHG_DO), InvalidOperation);
  151. EXPECT_THROW(stats_mgr->getStdDevDelay(StatsMgr4::XCHG_DO),
  152. InvalidOperation);
  153. EXPECT_THROW(stats_mgr->getAvgUnorderedLookupSetSize(StatsMgr4::XCHG_DO),
  154. InvalidOperation);
  155. }
  156. TEST_F(StatsMgrTest, Exchange) {
  157. boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
  158. boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER,
  159. common_transid));
  160. boost::shared_ptr<Pkt4> rcvd_packet(createPacket4(DHCPOFFER,
  161. common_transid));
  162. // This is expected to throw because XCHG_DO was not yet
  163. // added to Stats Manager for tracking.
  164. EXPECT_THROW(
  165. stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet),
  166. BadValue
  167. );
  168. EXPECT_THROW(
  169. stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet),
  170. BadValue
  171. );
  172. // Adding DISCOVER-OFFER exchanges to be tracked by Stats Manager.
  173. stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
  174. // The following two attempts are expected to throw because
  175. // invalid exchange types are passed (XCHG_RA instead of XCHG_DO)
  176. EXPECT_THROW(
  177. stats_mgr->passSentPacket(StatsMgr4::XCHG_RA, sent_packet),
  178. BadValue
  179. );
  180. EXPECT_THROW(
  181. stats_mgr->passRcvdPacket(StatsMgr4::XCHG_RA, rcvd_packet),
  182. BadValue
  183. );
  184. // The following two attempts are expected to run fine because
  185. // right exchange type is specified.
  186. EXPECT_NO_THROW(
  187. stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet)
  188. );
  189. EXPECT_NO_THROW(
  190. stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet)
  191. );
  192. }
  193. TEST_F(StatsMgrTest, MultipleExchanges) {
  194. boost::shared_ptr<StatsMgr6> stats_mgr(new StatsMgr6());
  195. stats_mgr->addExchangeStats(StatsMgr6::XCHG_SA);
  196. stats_mgr->addExchangeStats(StatsMgr6::XCHG_RR);
  197. // Simulate sending number of solicit packets.
  198. const int solicit_packets_num = 10;
  199. passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_SA, DHCPV6_SOLICIT,
  200. solicit_packets_num);
  201. // Simulate sending number of request packets. It is important that
  202. // number of request packets is different then number of solicit
  203. // packets. We can now check if right number packets went to
  204. // the right exchange type group.
  205. const int request_packets_num = 5;
  206. passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_RR, DHCPV6_REQUEST,
  207. request_packets_num);
  208. // Check if all packets are successfuly passed to packet lists.
  209. EXPECT_EQ(solicit_packets_num,
  210. stats_mgr->getSentPacketsNum(StatsMgr6::XCHG_SA));
  211. EXPECT_EQ(request_packets_num,
  212. stats_mgr->getSentPacketsNum(StatsMgr6::XCHG_RR));
  213. // Simulate reception of multiple packets for both SOLICIT-ADVERTISE
  214. // and REQUEST-REPLY exchanges. Assume no packet drops.
  215. const bool receive_packets = true;
  216. passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_SA, DHCPV6_ADVERTISE,
  217. solicit_packets_num, receive_packets);
  218. passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_RR, DHCPV6_REPLY,
  219. request_packets_num, receive_packets);
  220. // Verify that all received packets are counted.
  221. EXPECT_EQ(solicit_packets_num,
  222. stats_mgr->getRcvdPacketsNum(StatsMgr6::XCHG_SA));
  223. EXPECT_EQ(request_packets_num,
  224. stats_mgr->getRcvdPacketsNum(StatsMgr6::XCHG_RR));
  225. }
  226. TEST_F(StatsMgrTest, SendReceiveSimple) {
  227. boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
  228. boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER,
  229. common_transid));
  230. boost::shared_ptr<Pkt4> rcvd_packet(createPacket4(DHCPOFFER,
  231. common_transid));
  232. stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
  233. // The following attempt is expected to pass because the right
  234. // exchange type is used.
  235. ASSERT_NO_THROW(
  236. stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet)
  237. );
  238. // It is ok, to pass to received packets here. First one will
  239. // be matched with sent packet. The latter one will not be
  240. // matched with sent packet but orphans counter will simply
  241. // increase.
  242. ASSERT_NO_THROW(
  243. stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet)
  244. );
  245. ASSERT_NO_THROW(
  246. stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet)
  247. );
  248. EXPECT_EQ(1, stats_mgr->getOrphans(StatsMgr4::XCHG_DO));
  249. }
  250. TEST_F(StatsMgrTest, SendReceiveUnordered) {
  251. const int packets_num = 10;
  252. boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
  253. stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
  254. // Transaction ids of 10 packets to be sent and received.
  255. uint32_t transid[packets_num] =
  256. { 1, 1024, 2, 1025, 3, 1026, 4, 1027, 5, 1028 };
  257. for (int i = 0; i < packets_num; ++i) {
  258. boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER,
  259. transid[i]));
  260. ASSERT_NO_THROW(
  261. stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet)
  262. );
  263. }
  264. // We are simulating that received packets are coming in reverse order:
  265. // 1028, 5, 1027 ....
  266. for (int i = 0; i < packets_num; ++i) {
  267. boost::shared_ptr<Pkt4>
  268. rcvd_packet(createPacket4(DHCPDISCOVER,
  269. transid[packets_num - 1 - i]));
  270. ASSERT_NO_THROW(
  271. stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet);
  272. );
  273. }
  274. // All packets are expected to match (we did not drop any)
  275. EXPECT_EQ(0, stats_mgr->getOrphans(StatsMgr4::XCHG_DO));
  276. // Most of the time we have to do unordered lookups except for the last
  277. // one. Packets are removed from the sent list every time we have a match
  278. // so eventually we come up with the single packet that caching iterator
  279. // is pointing to. This is counted as ordered lookup.
  280. EXPECT_EQ(1, stats_mgr->getOrderedLookups(StatsMgr4::XCHG_DO));
  281. EXPECT_EQ(9, stats_mgr->getUnorderedLookups(StatsMgr4::XCHG_DO));
  282. }
  283. TEST_F(StatsMgrTest, Orphans) {
  284. const int packets_num = 6;
  285. boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
  286. stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
  287. // We skip every second packet to simulate drops.
  288. for (int i = 0; i < packets_num; i += 2) {
  289. boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER, i));
  290. ASSERT_NO_THROW(
  291. stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet)
  292. );
  293. }
  294. // We pass all received packets.
  295. for (int i = 0; i < packets_num; ++i) {
  296. boost::shared_ptr<Pkt4> rcvd_packet(createPacket4(DHCPOFFER, i));
  297. ASSERT_NO_THROW(
  298. stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet);
  299. );
  300. }
  301. // The half of received packets are expected not to have matching
  302. // sent packet.
  303. EXPECT_EQ(packets_num / 2, stats_mgr->getOrphans(StatsMgr4::XCHG_DO));
  304. }
  305. TEST_F(StatsMgrTest, Delays) {
  306. boost::shared_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
  307. stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO, 5);
  308. // Send DISCOVER, wait 2s and receive OFFER. This will affect
  309. // counters in Stats Manager.
  310. const unsigned int delay1 = 2;
  311. passDOPacketsWithDelay(stats_mgr, 2, common_transid);
  312. // Initially min delay is equal to MAX_DOUBLE. After first packets
  313. // are passed, it is expected to set to actual value.
  314. EXPECT_LT(stats_mgr->getMinDelay(StatsMgr4::XCHG_DO),
  315. std::numeric_limits<double>::max());
  316. EXPECT_GT(stats_mgr->getMinDelay(StatsMgr4::XCHG_DO), 1);
  317. // Max delay is supposed to the same value as mininimum
  318. // or maximum delay.
  319. EXPECT_GT(stats_mgr->getMaxDelay(StatsMgr4::XCHG_DO), 1);
  320. // Delay sums are now the same as minimum or maximum delay.
  321. EXPECT_GT(stats_mgr->getAvgDelay(StatsMgr4::XCHG_DO), 1);
  322. // Simulate another DISCOVER-OFFER exchange with delay between
  323. // sent and received packets. Delay is now shorter than earlier
  324. // so standard deviation of delay will now increase.
  325. const unsigned int delay2 = 1;
  326. passDOPacketsWithDelay(stats_mgr, delay2, common_transid + 1);
  327. // Standard deviation is expected to be non-zero.
  328. EXPECT_GT(stats_mgr->getStdDevDelay(StatsMgr4::XCHG_DO), 0);
  329. }
  330. TEST_F(StatsMgrTest, CustomCounters) {
  331. boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
  332. // Specify counter keys and names.
  333. const std::string too_short_key("tooshort");
  334. const std::string too_short_name("Too short packets");
  335. const std::string too_late_key("toolate");
  336. const std::string too_late_name("Packets sent too late");
  337. // Add two custom counters.
  338. stats_mgr->addCustomCounter(too_short_key, too_short_name);
  339. stats_mgr->addCustomCounter(too_late_key, too_late_name);
  340. // Increment one of the counters 10 times.
  341. const uint64_t tooshort_num = 10;
  342. for (uint64_t i = 0; i < tooshort_num; ++i) {
  343. stats_mgr->incrementCounter(too_short_key);
  344. }
  345. // Increment another counter by 5 times.
  346. const uint64_t toolate_num = 5;
  347. for (uint64_t i = 0; i < toolate_num; ++i) {
  348. stats_mgr->incrementCounter(too_late_key);
  349. }
  350. // Check counter's current value and name.
  351. StatsMgr4::CustomCounterPtr tooshort_counter =
  352. stats_mgr->getCounter(too_short_key);
  353. EXPECT_EQ(too_short_name, tooshort_counter->getName());
  354. EXPECT_EQ(tooshort_num, tooshort_counter->getValue());
  355. // Check counter's current value and name.
  356. StatsMgr4::CustomCounterPtr toolate_counter =
  357. stats_mgr->getCounter(too_late_key);
  358. EXPECT_EQ(too_late_name, toolate_counter->getName());
  359. EXPECT_EQ(toolate_num, toolate_counter->getValue());
  360. }
  361. TEST_F(StatsMgrTest, PrintStats) {
  362. std::cout << "This unit test is checking statistics printing "
  363. << "capabilities. It is expected that some counters "
  364. << "will be printed during this test. It may also "
  365. << "cause spurious errors." << std::endl;
  366. boost::shared_ptr<StatsMgr6> stats_mgr(new StatsMgr6());
  367. stats_mgr->addExchangeStats(StatsMgr6::XCHG_SA);
  368. // Simulate sending and receiving one packet. Otherwise printing
  369. // functions will complain about lack of packets.
  370. const int packets_num = 1;
  371. passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_SA, DHCPV6_SOLICIT,
  372. packets_num);
  373. passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_SA, DHCPV6_ADVERTISE,
  374. packets_num, true);
  375. // This function will print statistics even if packets are not
  376. // archived because it relies on counters. There is at least one
  377. // exchange needed to count the average delay and std deviation.
  378. EXPECT_NO_THROW(stats_mgr->printStats());
  379. // Printing timestamps is expected to fail because by default we
  380. // disable packets archiving mode. Without packets we can't get
  381. // timestamps.
  382. EXPECT_THROW(stats_mgr->printTimestamps(), isc::InvalidOperation);
  383. // Now, we create another statistics manager instance and enable
  384. // packets archiving mode.
  385. const bool archive_packets = true;
  386. boost::shared_ptr<StatsMgr6> stats_mgr2(new StatsMgr6(archive_packets));
  387. stats_mgr2->addExchangeStats(StatsMgr6::XCHG_SA);
  388. // Timestamps should now get printed because packets have been preserved.
  389. EXPECT_NO_THROW(stats_mgr2->printTimestamps());
  390. }
  391. }