stats_mgr_unittest.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  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_THROW(stats_mgr->getAvgDelay(StatsMgr4::XCHG_DO), InvalidOperation);
  150. EXPECT_THROW(stats_mgr->getStdDevDelay(StatsMgr4::XCHG_DO),
  151. InvalidOperation);
  152. EXPECT_THROW(stats_mgr->getAvgUnorderedLookupSetSize(StatsMgr4::XCHG_DO),
  153. InvalidOperation);
  154. }
  155. TEST_F(StatsMgrTest, Exchange) {
  156. boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
  157. boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER,
  158. common_transid));
  159. boost::shared_ptr<Pkt4> rcvd_packet(createPacket4(DHCPOFFER,
  160. common_transid));
  161. // This is expected to throw because XCHG_DO was not yet
  162. // added to Stats Manager for tracking.
  163. EXPECT_THROW(
  164. stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet),
  165. BadValue
  166. );
  167. EXPECT_THROW(
  168. stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet),
  169. BadValue
  170. );
  171. // Adding DISCOVER-OFFER exchanges to be tracked by Stats Manager.
  172. stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
  173. // The following two attempts are expected to throw because
  174. // invalid exchange types are passed (XCHG_RA instead of XCHG_DO)
  175. EXPECT_THROW(
  176. stats_mgr->passSentPacket(StatsMgr4::XCHG_RA, sent_packet),
  177. BadValue
  178. );
  179. EXPECT_THROW(
  180. stats_mgr->passRcvdPacket(StatsMgr4::XCHG_RA, rcvd_packet),
  181. BadValue
  182. );
  183. // The following two attempts are expected to run fine because
  184. // right exchange type is specified.
  185. EXPECT_NO_THROW(
  186. stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet)
  187. );
  188. EXPECT_NO_THROW(
  189. stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet)
  190. );
  191. }
  192. TEST_F(StatsMgrTest, MultipleExchanges) {
  193. boost::shared_ptr<StatsMgr6> stats_mgr(new StatsMgr6());
  194. stats_mgr->addExchangeStats(StatsMgr6::XCHG_SA);
  195. stats_mgr->addExchangeStats(StatsMgr6::XCHG_RR);
  196. // Simulate sending number of solicit packets.
  197. const int solicit_packets_num = 10;
  198. passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_SA, DHCPV6_SOLICIT,
  199. solicit_packets_num);
  200. // Simulate sending number of request packets. It is important that
  201. // number of request packets is different then number of solicit
  202. // packets. We can now check if right number packets went to
  203. // the right exchange type group.
  204. const int request_packets_num = 5;
  205. passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_RR, DHCPV6_REQUEST,
  206. request_packets_num);
  207. // Check if all packets are successfuly passed to packet lists.
  208. EXPECT_EQ(solicit_packets_num,
  209. stats_mgr->getSentPacketsNum(StatsMgr6::XCHG_SA));
  210. EXPECT_EQ(request_packets_num,
  211. stats_mgr->getSentPacketsNum(StatsMgr6::XCHG_RR));
  212. // Simulate reception of multiple packets for both SOLICIT-ADVERTISE
  213. // and REQUEST-REPLY exchanges. Assume no packet drops.
  214. const bool receive_packets = true;
  215. passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_SA, DHCPV6_ADVERTISE,
  216. solicit_packets_num, receive_packets);
  217. passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_RR, DHCPV6_REPLY,
  218. request_packets_num, receive_packets);
  219. // Verify that all received packets are counted.
  220. EXPECT_EQ(solicit_packets_num,
  221. stats_mgr->getRcvdPacketsNum(StatsMgr6::XCHG_SA));
  222. EXPECT_EQ(request_packets_num,
  223. stats_mgr->getRcvdPacketsNum(StatsMgr6::XCHG_RR));
  224. }
  225. TEST_F(StatsMgrTest, SendReceiveSimple) {
  226. boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
  227. boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER,
  228. common_transid));
  229. boost::shared_ptr<Pkt4> rcvd_packet(createPacket4(DHCPOFFER,
  230. common_transid));
  231. stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
  232. // The following attempt is expected to pass becase the right
  233. // exchange type is used.
  234. ASSERT_NO_THROW(
  235. stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet)
  236. );
  237. // It is ok, to pass to received packets here. First one will
  238. // be matched with sent packet. The latter one will not be
  239. // matched with sent packet but orphans counter will simply
  240. // increase.
  241. ASSERT_NO_THROW(
  242. stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet)
  243. );
  244. ASSERT_NO_THROW(
  245. stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet)
  246. );
  247. EXPECT_EQ(1, stats_mgr->getOrphans(StatsMgr4::XCHG_DO));
  248. }
  249. TEST_F(StatsMgrTest, SendReceiveUnordered) {
  250. const int packets_num = 10;
  251. boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
  252. stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
  253. // Transaction ids of 10 packets to be sent and received.
  254. uint32_t transid[packets_num] =
  255. { 1, 1024, 2, 1025, 3, 1026, 4, 1027, 5, 1028 };
  256. for (int i = 0; i < packets_num; ++i) {
  257. boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER,
  258. transid[i]));
  259. ASSERT_NO_THROW(
  260. stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet)
  261. );
  262. }
  263. // We are simulating that received packets are coming in reverse order:
  264. // 1028, 5, 1027 ....
  265. for (int i = 0; i < packets_num; ++i) {
  266. boost::shared_ptr<Pkt4>
  267. rcvd_packet(createPacket4(DHCPDISCOVER,
  268. transid[packets_num - 1 - i]));
  269. ASSERT_NO_THROW(
  270. stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet);
  271. );
  272. }
  273. // All packets are expected to match (we did not drop any)
  274. EXPECT_EQ(0, stats_mgr->getOrphans(StatsMgr4::XCHG_DO));
  275. // Most of the time we have to do unordered lookups except for the last
  276. // one. Packets are removed from the sent list every time we have a match
  277. // so eventually we come up with the single packet that caching iterator
  278. // is pointing to. This is counted as ordered lookup.
  279. EXPECT_EQ(1, stats_mgr->getOrderedLookups(StatsMgr4::XCHG_DO));
  280. EXPECT_EQ(9, stats_mgr->getUnorderedLookups(StatsMgr4::XCHG_DO));
  281. }
  282. TEST_F(StatsMgrTest, Orphans) {
  283. const int packets_num = 6;
  284. boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
  285. stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
  286. // We skip every second packet to simulate drops.
  287. for (int i = 0; i < packets_num; i += 2) {
  288. boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER, i));
  289. ASSERT_NO_THROW(
  290. stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet)
  291. );
  292. }
  293. // We pass all received packets.
  294. for (int i = 0; i < packets_num; ++i) {
  295. boost::shared_ptr<Pkt4> rcvd_packet(createPacket4(DHCPOFFER, i));
  296. ASSERT_NO_THROW(
  297. stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet);
  298. );
  299. }
  300. // The half of received packets are expected not to have matching
  301. // sent packet.
  302. EXPECT_EQ(packets_num / 2, stats_mgr->getOrphans(StatsMgr4::XCHG_DO));
  303. }
  304. TEST_F(StatsMgrTest, Delays) {
  305. boost::shared_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
  306. stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
  307. // Send DISCOVER, wait 2s and receive OFFER. This will affect
  308. // counters in Stats Manager.
  309. const unsigned int delay1 = 2;
  310. passDOPacketsWithDelay(stats_mgr, 2, common_transid);
  311. // Initially min delay is equal to MAX_DOUBLE. After first packets
  312. // are passed, it is expected to set to actual value.
  313. EXPECT_LT(stats_mgr->getMinDelay(StatsMgr4::XCHG_DO),
  314. std::numeric_limits<double>::max());
  315. EXPECT_GT(stats_mgr->getMinDelay(StatsMgr4::XCHG_DO), 1);
  316. // Max delay is supposed to the same value as mininimum
  317. // or maximum delay.
  318. EXPECT_GT(stats_mgr->getMaxDelay(StatsMgr4::XCHG_DO), 1);
  319. // Delay sums are now the same as minimum or maximum delay.
  320. EXPECT_GT(stats_mgr->getAvgDelay(StatsMgr4::XCHG_DO), 1);
  321. // Simulate another DISCOVER-OFFER exchange with delay between
  322. // sent and received packets. Delay is now shorter than earlier
  323. // so standard deviation of delay will now increase.
  324. const unsigned int delay2 = 1;
  325. passDOPacketsWithDelay(stats_mgr, delay2, common_transid + 1);
  326. // Standard deviation is expected to be non-zero.
  327. EXPECT_GT(stats_mgr->getStdDevDelay(StatsMgr4::XCHG_DO), 0);
  328. }
  329. TEST_F(StatsMgrTest, CustomCounters) {
  330. boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
  331. // Specify counter keys and names.
  332. const std::string too_short_key("tooshort");
  333. const std::string too_short_name("Too short packets");
  334. const std::string too_late_key("toolate");
  335. const std::string too_late_name("Packets sent too late");
  336. // Add two custom counters.
  337. stats_mgr->addCustomCounter(too_short_key, too_short_name);
  338. stats_mgr->addCustomCounter(too_late_key, too_late_name);
  339. // Increment one of the counters 10 times.
  340. const uint64_t tooshort_num = 10;
  341. for (uint64_t i = 0; i < tooshort_num; ++i) {
  342. stats_mgr->IncrementCounter(too_short_key);
  343. }
  344. // Increment another counter by 5 times.
  345. const uint64_t toolate_num = 5;
  346. for (uint64_t i = 0; i < toolate_num; ++i) {
  347. stats_mgr->IncrementCounter(too_late_key);
  348. }
  349. // Check counter's current value and name.
  350. StatsMgr4::CustomCounterPtr tooshort_counter =
  351. stats_mgr->getCounter(too_short_key);
  352. EXPECT_EQ(too_short_name, tooshort_counter->getName());
  353. EXPECT_EQ(tooshort_num, tooshort_counter->getValue());
  354. // Check counter's current value and name.
  355. StatsMgr4::CustomCounterPtr toolate_counter =
  356. stats_mgr->getCounter(too_late_key);
  357. EXPECT_EQ(too_late_name, toolate_counter->getName());
  358. EXPECT_EQ(toolate_num, toolate_counter->getValue());
  359. }
  360. TEST_F(StatsMgrTest, PrintStats) {
  361. std::cout << "This unit test is checking statistics printing "
  362. << "capabilities. It is expected that some counters "
  363. << "will be printed during this test. It may also "
  364. << "cause spurious errors." << std::endl;
  365. boost::shared_ptr<StatsMgr6> stats_mgr(new StatsMgr6());
  366. stats_mgr->addExchangeStats(StatsMgr6::XCHG_SA);
  367. // Simulate sending and receiving one packet. Otherwise printing
  368. // functions will complain about lack of packets.
  369. const int packets_num = 1;
  370. passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_SA, DHCPV6_SOLICIT,
  371. packets_num);
  372. passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_SA, DHCPV6_ADVERTISE,
  373. packets_num, true);
  374. // This function will print statistics even if packets are not
  375. // archived because it relies on counters. There is at least one
  376. // exchange needed to count the average delay and std deviation.
  377. EXPECT_NO_THROW(stats_mgr->printStats());
  378. // Printing timestamps is expected to fail because by default we
  379. // disable packets archiving mode. Without packets we can't get
  380. // timestamps.
  381. EXPECT_THROW(stats_mgr->printTimestamps(), isc::InvalidOperation);
  382. // Now, we create another statistics manager instance and enable
  383. // packets archiving mode.
  384. const bool archive_packets = true;
  385. boost::shared_ptr<StatsMgr6> stats_mgr2(new StatsMgr6(archive_packets));
  386. stats_mgr2->addExchangeStats(StatsMgr6::XCHG_SA);
  387. // Timestamps should now get printed because packets have been preserved.
  388. EXPECT_NO_THROW(stats_mgr2->printTimestamps());
  389. }
  390. }