From: Andrei Pavel Date: Fri, 28 Apr 2023 12:17:11 +0000 (+0300) Subject: [#2797] add unit tests for lease reuse statistics X-Git-Tag: Kea-2.3.8~193 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8000f423dd1df893fe621b407b7d69a1c87a315c;p=thirdparty%2Fkea.git [#2797] add unit tests for lease reuse statistics --- diff --git a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc index f827f0ebff..02167097a1 100644 --- a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc @@ -647,7 +647,8 @@ TEST_F(CtrlChannelDhcpv4SrvTest, controlChannelStats) { "v4-allocation-fail-subnet", "v4-allocation-fail-no-pools", "v4-allocation-fail-classes", - "v4-reservation-conflicts" + "v4-reservation-conflicts", + "v4-lease-reuses", }; // preparing the schema which check if all statistics are set to zero diff --git a/src/bin/dhcp4/tests/dora_unittest.cc b/src/bin/dhcp4/tests/dora_unittest.cc index 801647ec97..abba841404 100644 --- a/src/bin/dhcp4/tests/dora_unittest.cc +++ b/src/bin/dhcp4/tests/dora_unittest.cc @@ -579,7 +579,32 @@ const char* DORA_CONFIGS[] = { " \"interface\": \"eth0\"" " }" "]" - "}" + "}", + + // Configuration 18 + R"({ + "cache-max-age": 600, + "cache-threshold": .50, + "interfaces-config": { + "interfaces": [ "*" ] + }, + "subnet4": [ + { + "id": 1, + "pools": [ + { + "pool": "10.0.0.10 - 10.0.0.20" + }, + ], + "subnet": "10.0.0.0/24" + }, + { + "id": 2, + "subnet": "192.168.0.0/24" + } + ], + "valid-lifetime": 600 + })", }; /// @brief Test fixture class for testing 4-way (DORA) exchanges. @@ -831,6 +856,9 @@ public: /// to the configuration and it allocates random addresses. void randomAllocation(); + /// @brief Checks that features related to lease caching (such as lease reuse statistics) work. + void leaseCaching(); + /// @brief Interface Manager's fake configuration control. IfaceMgrTestConfig iface_mgr_test_config_; }; @@ -2800,6 +2828,163 @@ TEST_F(DORATest, randomAllocationMultiThreading) { randomAllocation(); } +void +DORATest::leaseCaching() { + // Configure a DHCP client. + Dhcp4Client client; + client.includeClientId("11:22"); + + // Configure a DHCP server. + configure(DORA_CONFIGS[18], *client.getServer()); + + // Obtain a lease from the server using the 4-way exchange. + ASSERT_NO_THROW(client.doDORA()); + + // Make sure that the server responded. + Pkt4Ptr response(client.getContext().response_); + ASSERT_TRUE(response); + + // Make sure that the server has responded with DHCPACK. + EXPECT_EQ(DHCPACK, response->getType()); + + // Make sure that the server ID is present. + EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText()); + + // Make sure that the client has got the first lease in the pool. + ASSERT_EQ("10.0.0.10", client.config_.lease_.addr_.toText()); + + // There should only be the default global statistic point. + ObservationPtr lease_reuses(StatsMgr::instance().getObservation("v4-lease-reuses")); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(1, lease_reuses->getSize()); + EXPECT_EQ(0, lease_reuses->getInteger().first); + + // There should only be the default subnet statistic point. + lease_reuses = StatsMgr::instance().getObservation("subnet[1].v4-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(1, lease_reuses->getSize()); + EXPECT_EQ(0, lease_reuses->getInteger().first); + + // There should only be the default statistic point in the other subnet. + lease_reuses = StatsMgr::instance().getObservation("subnet[2].v4-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(1, lease_reuses->getSize()); + EXPECT_EQ(0, lease_reuses->getInteger().first); + + // Assume the client enters init-reboot and does a request. + client.setState(Dhcp4Client::INIT_REBOOT); + ASSERT_NO_THROW(client.doRequest()); + + // Make sure that the server responded. + response = client.getContext().response_; + ASSERT_TRUE(response); + + // Make sure that the server has responded with DHCPACK. + EXPECT_EQ(DHCPACK, response->getType()); + + // Make sure that the server ID is present. + EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText()); + + // Make sure that the client has got the same lease. + ASSERT_EQ("10.0.0.10", client.config_.lease_.addr_.toText()); + + // There should be one global lease reuse. + lease_reuses = StatsMgr::instance().getObservation("v4-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(2, lease_reuses->getSize()); + EXPECT_EQ(1, lease_reuses->getInteger().first); + + // There should be one subnet lease reuse. + lease_reuses = StatsMgr::instance().getObservation("subnet[1].v4-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(2, lease_reuses->getSize()); + EXPECT_EQ(1, lease_reuses->getInteger().first); + + // Statistics for the other subnet should not be affected. + lease_reuses = StatsMgr::instance().getObservation("subnet[2].v4-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(1, lease_reuses->getSize()); + EXPECT_EQ(0, lease_reuses->getInteger().first); + + // Let's say the client suddenly decides to do a full DORA. + ASSERT_NO_THROW(client.doDORA()); + + // Make sure that the server responded. + response = client.getContext().response_; + ASSERT_TRUE(response); + + // Make sure that the server has responded with DHCPACK. + EXPECT_EQ(DHCPACK, response->getType()); + + // Make sure that the server ID is present. + EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText()); + + // Make sure that the client has got the same lease. + ASSERT_EQ("10.0.0.10", client.config_.lease_.addr_.toText()); + + // There should be three global lease reuses (REQUEST + DISCOVER + REQUEST). + lease_reuses = StatsMgr::instance().getObservation("v4-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(4, lease_reuses->getSize()); + EXPECT_EQ(3, lease_reuses->getInteger().first); + + // There should be three subnet lease reuses (REQUEST + DISCOVER + REQUEST). + lease_reuses = StatsMgr::instance().getObservation("subnet[1].v4-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(4, lease_reuses->getSize()); + EXPECT_EQ(3, lease_reuses->getInteger().first); + + // Statistics for the other subnet should not be affected. + lease_reuses = StatsMgr::instance().getObservation("subnet[2].v4-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(1, lease_reuses->getSize()); + EXPECT_EQ(0, lease_reuses->getInteger().first); + + // Try to request a different address than the client has. The server + // should respond with DHCPNAK. + client.config_.lease_.addr_ = IOAddress("10.0.0.30"); + ASSERT_NO_THROW(client.doRequest()); + + // Make sure that the server responded. + response = client.getContext().response_; + ASSERT_TRUE(response); + EXPECT_EQ(DHCPNAK, response->getType()); + + // Change client identifier. The server should treat the request + // as a request from unknown client and ignore it. + client.includeClientId("12:34"); + ASSERT_NO_THROW(client.doRequest()); + ASSERT_FALSE(client.getContext().response_); + + // Global statistics should remain unchanged. + lease_reuses = StatsMgr::instance().getObservation("v4-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(4, lease_reuses->getSize()); + EXPECT_EQ(3, lease_reuses->getInteger().first); + + // Subnet statistics should remain unchanged. + lease_reuses = StatsMgr::instance().getObservation("subnet[1].v4-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(4, lease_reuses->getSize()); + EXPECT_EQ(3, lease_reuses->getInteger().first); + + // Statistics for the other subnet should certainly not be affected. + lease_reuses = StatsMgr::instance().getObservation("subnet[2].v4-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(1, lease_reuses->getSize()); + EXPECT_EQ(0, lease_reuses->getInteger().first); +} + +TEST_F(DORATest, leaseCaching) { + Dhcpv4SrvMTTestGuard guard(*this, false); + leaseCaching(); +} + +TEST_F(DORATest, leaseCachingMultiThreading) { + Dhcpv4SrvMTTestGuard guard(*this, true); + leaseCaching(); +} + // Starting tests which require MySQL backend availability. Those tests // will not be executed if Kea has been compiled without the // --with-mysql. diff --git a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc index a583751421..d1ed521713 100644 --- a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc @@ -1360,7 +1360,9 @@ TEST_F(CtrlChannelDhcpv6SrvTest, controlChannelStats) { "v6-allocation-fail-shared-network", "v6-allocation-fail-subnet", "v6-allocation-fail-no-pools", - "v6-allocation-fail-classes" + "v6-allocation-fail-classes", + "v6-ia-na-lease-reuses", + "v6-ia-pd-lease-reuses", }; std::ostringstream s; diff --git a/src/bin/dhcp6/tests/sarr_unittest.cc b/src/bin/dhcp6/tests/sarr_unittest.cc index 43dc922cee..ee4c8f69a8 100644 --- a/src/bin/dhcp6/tests/sarr_unittest.cc +++ b/src/bin/dhcp6/tests/sarr_unittest.cc @@ -21,6 +21,7 @@ using namespace isc; using namespace isc::asiolink; using namespace isc::dhcp; using namespace isc::dhcp::test; +using namespace isc::stats; namespace { @@ -286,6 +287,39 @@ const char* CONFIGS[] = { " }" "]," "\"valid-lifetime\": 4000 }", + + // Configuration 7 + R"({ + "cache-max-age": 600, + "cache-threshold": .50, + "interfaces-config": { + "interfaces": [ "*" ] + }, + "subnet6": [ + { + "id": 1, + "interface": "eth0", + "pools": [ + { + "pool": "2001:db8::10 - 2001:db8::20" + }, + ], + "pd-pools": [ + { + "prefix": "2001:db8:1::", + "prefix-len": 64, + "delegated-len": 96 + }, + ], + "subnet": "2001:db8::/32" + }, + { + "id": 2, + "subnet": "3001:db8::/32" + } + ], + "valid-lifetime": 600 + })", }; /// @brief Test fixture class for testing 4-way exchange: Solicit-Advertise, @@ -387,6 +421,9 @@ public: /// to the configuration and it allocates random prefixes. void randomPrefixAllocation(); + /// @brief Checks that features related to lease caching (such as lease reuse statistics) work. + void leaseCaching(); + /// @brief Interface Manager's fake configuration control. IfaceMgrTestConfig iface_mgr_test_config_; }; @@ -1159,5 +1196,177 @@ TEST_F(SARRTest, randomPrefixAllocationMultiThreading) { randomPrefixAllocation(); } +void +SARRTest::leaseCaching() { + // Configure a DHCP client. + Dhcp6Client client; + + // Configure a DHCP server. + configure(CONFIGS[7], *client.getServer()); + + // Statistics should have default values. + ObservationPtr lease_reuses(StatsMgr::instance().getObservation("v6-ia-na-lease-reuses")); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(1, lease_reuses->getSize()); + EXPECT_EQ(0, lease_reuses->getInteger().first); + lease_reuses = StatsMgr::instance().getObservation("subnet[1].v6-ia-na-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(1, lease_reuses->getSize()); + EXPECT_EQ(0, lease_reuses->getInteger().first); + lease_reuses = StatsMgr::instance().getObservation("subnet[2].v6-ia-na-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(1, lease_reuses->getSize()); + EXPECT_EQ(0, lease_reuses->getInteger().first); + lease_reuses = StatsMgr::instance().getObservation("v6-ia-pd-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(1, lease_reuses->getSize()); + EXPECT_EQ(0, lease_reuses->getInteger().first); + lease_reuses = StatsMgr::instance().getObservation("subnet[1].v6-ia-pd-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(1, lease_reuses->getSize()); + EXPECT_EQ(0, lease_reuses->getInteger().first); + lease_reuses = StatsMgr::instance().getObservation("subnet[2].v6-ia-pd-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(1, lease_reuses->getSize()); + EXPECT_EQ(0, lease_reuses->getInteger().first); + + // Append IAADDR and IAPREFIX options to the client's message. + ASSERT_NO_THROW(client.requestAddress(1234, asiolink::IOAddress("2001:db8::10"))); + ASSERT_NO_THROW(client.requestPrefix(5678, 32, asiolink::IOAddress("2001:db8:1::"))); + + // Perform 4-way exchange. + ASSERT_NO_THROW(client.doSARR()); + + // Server should have assigned an address and a prefix. + ASSERT_EQ(2, client.getLeaseNum()); + + // The server should respect the hints. + Lease6 lease_client(client.getLease(0)); + EXPECT_EQ("2001:db8::10", lease_client.addr_.toText()); + EXPECT_EQ(128, lease_client.prefixlen_); + Lease6Ptr lease_server(checkLease(lease_client)); + EXPECT_TRUE(lease_server); + lease_client = client.getLease(1); + EXPECT_EQ("2001:db8:1::", lease_client.addr_.toText()); + EXPECT_EQ(96, lease_client.prefixlen_); + lease_server = checkLease(lease_client); + EXPECT_TRUE(lease_server); + + // Check statistics. + lease_reuses = StatsMgr::instance().getObservation("v6-ia-na-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(1, lease_reuses->getSize()); + EXPECT_EQ(0, lease_reuses->getInteger().first); + lease_reuses = StatsMgr::instance().getObservation("subnet[1].v6-ia-na-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(1, lease_reuses->getSize()); + EXPECT_EQ(0, lease_reuses->getInteger().first); + lease_reuses = StatsMgr::instance().getObservation("subnet[2].v6-ia-na-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(1, lease_reuses->getSize()); + EXPECT_EQ(0, lease_reuses->getInteger().first); + lease_reuses = StatsMgr::instance().getObservation("v6-ia-pd-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(1, lease_reuses->getSize()); + EXPECT_EQ(0, lease_reuses->getInteger().first); + lease_reuses = StatsMgr::instance().getObservation("subnet[1].v6-ia-pd-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(1, lease_reuses->getSize()); + EXPECT_EQ(0, lease_reuses->getInteger().first); + lease_reuses = StatsMgr::instance().getObservation("subnet[2].v6-ia-pd-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(1, lease_reuses->getSize()); + EXPECT_EQ(0, lease_reuses->getInteger().first); + + // Request the same prefix with a different length. The server should + // return an existing lease. + client.clearRequestedIAs(); + ASSERT_NO_THROW(client.requestAddress(1234, asiolink::IOAddress("2001:db8::10"))); + ASSERT_NO_THROW(client.requestPrefix(5678, 80, IOAddress("2001:db8:1::"))); + ASSERT_NO_THROW(client.doSARR()); + ASSERT_EQ(2, client.getLeaseNum()); + lease_client = client.getLease(0); + EXPECT_EQ("2001:db8::10", lease_client.addr_.toText()); + EXPECT_EQ(128, lease_client.prefixlen_); + lease_client = client.getLease(1); + EXPECT_EQ("2001:db8:1::", lease_client.addr_.toText()); + EXPECT_EQ(96, lease_client.prefixlen_); + + // Check statistics. + lease_reuses = StatsMgr::instance().getObservation("v6-ia-na-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(2, lease_reuses->getSize()); + EXPECT_EQ(1, lease_reuses->getInteger().first); + lease_reuses = StatsMgr::instance().getObservation("subnet[1].v6-ia-na-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(2, lease_reuses->getSize()); + EXPECT_EQ(1, lease_reuses->getInteger().first); + lease_reuses = StatsMgr::instance().getObservation("subnet[2].v6-ia-na-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(1, lease_reuses->getSize()); + EXPECT_EQ(0, lease_reuses->getInteger().first); + lease_reuses = StatsMgr::instance().getObservation("v6-ia-pd-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(2, lease_reuses->getSize()); + EXPECT_EQ(1, lease_reuses->getInteger().first); + lease_reuses = StatsMgr::instance().getObservation("subnet[1].v6-ia-pd-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(2, lease_reuses->getSize()); + EXPECT_EQ(1, lease_reuses->getInteger().first); + lease_reuses = StatsMgr::instance().getObservation("subnet[2].v6-ia-pd-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(1, lease_reuses->getSize()); + EXPECT_EQ(0, lease_reuses->getInteger().first); + + // Try to request another prefix. The client should still get the existing + // lease. + client.clearRequestedIAs(); + ASSERT_NO_THROW(client.requestAddress(1234, asiolink::IOAddress("2001:db8::10"))); + ASSERT_NO_THROW(client.requestPrefix(5678, 64, IOAddress("2001:db8:2::"))); + ASSERT_NO_THROW(client.doRequest()); + ASSERT_EQ(2, client.getLeaseNum()); + lease_client = client.getLease(0); + EXPECT_EQ("2001:db8::10", lease_client.addr_.toText()); + EXPECT_EQ(128, lease_client.prefixlen_); + lease_client = client.getLease(1); + EXPECT_EQ("2001:db8:1::", lease_client.addr_.toText()); + EXPECT_EQ(96, lease_client.prefixlen_); + + // Check statistics. + lease_reuses = StatsMgr::instance().getObservation("v6-ia-na-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(3, lease_reuses->getSize()); + EXPECT_EQ(2, lease_reuses->getInteger().first); + lease_reuses = StatsMgr::instance().getObservation("subnet[1].v6-ia-na-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(3, lease_reuses->getSize()); + EXPECT_EQ(2, lease_reuses->getInteger().first); + lease_reuses = StatsMgr::instance().getObservation("subnet[2].v6-ia-na-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(1, lease_reuses->getSize()); + EXPECT_EQ(0, lease_reuses->getInteger().first); + lease_reuses = StatsMgr::instance().getObservation("v6-ia-pd-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(3, lease_reuses->getSize()); + EXPECT_EQ(2, lease_reuses->getInteger().first); + lease_reuses = StatsMgr::instance().getObservation("subnet[1].v6-ia-pd-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(3, lease_reuses->getSize()); + EXPECT_EQ(2, lease_reuses->getInteger().first); + lease_reuses = StatsMgr::instance().getObservation("subnet[2].v6-ia-pd-lease-reuses"); + ASSERT_TRUE(lease_reuses); + EXPECT_EQ(1, lease_reuses->getSize()); + EXPECT_EQ(0, lease_reuses->getInteger().first); +} + +TEST_F(SARRTest, leaseCaching) { + Dhcpv6SrvMTTestGuard guard(*this, false); + leaseCaching(); +} + +TEST_F(SARRTest, leaseCachingMultiThreading) { + Dhcpv6SrvMTTestGuard guard(*this, true); + leaseCaching(); +} } // end of anonymous namespace