From: Thomas Markwalder Date: Tue, 7 Mar 2023 16:55:56 +0000 (-0500) Subject: [#2719] Added an additional UT X-Git-Tag: Kea-2.3.6~55 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0228ef224e087a4edcc5432d1e2cbe1f0faaa6a0;p=thirdparty%2Fkea.git [#2719] Added an additional UT src/bin/dhcp4/tests/fqdn_unittest.cc TEST_F(NameDhcpv4SrvTest, withOfferLifetime) --- diff --git a/src/bin/dhcp4/tests/fqdn_unittest.cc b/src/bin/dhcp4/tests/fqdn_unittest.cc index 5bba4ed022..89e98ca733 100644 --- a/src/bin/dhcp4/tests/fqdn_unittest.cc +++ b/src/bin/dhcp4/tests/fqdn_unittest.cc @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -16,6 +17,7 @@ #include #include #include +#include #include #include @@ -26,6 +28,7 @@ using namespace isc::asiolink; using namespace isc::dhcp; using namespace isc::dhcp::test; using namespace isc::dhcp_ddns; +using namespace isc::stats; namespace { @@ -299,6 +302,24 @@ const char* CONFIGS[] = { " \"ddns-qualifying-suffix\": \"two.example.com.\" \n" " }] \n" " }] \n" + "}", + // 11 + // D2 enabled + // offer-lifetime > 0 + "{ \"interfaces-config\": {\n" + " \"interfaces\": [ \"*\" ]\n" + "},\n" + "\"valid-lifetime\": 3000,\n" + "\"subnet4\": [ { \n" + " \"subnet\": \"10.0.0.0/24\", \n" + " \"id\": 1,\n" + " \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.10\" } ],\n" + " \"offer-lifetime\": 45,\n" + "} ],\n" + "\"dhcp-ddns\": {\n" + "\"enable-updates\": true,\n" + "\"qualifying-suffix\": \"example.com.\"\n" + "}\n" "}" }; @@ -341,6 +362,8 @@ public: IfaceMgr::instance().openSockets4(); // Config DDNS to be enabled, all controls off enableD2(); + // Let's wipe all existing statistics. + isc::stats::StatsMgr::instance().removeAll(); } virtual ~NameDhcpv4SrvTest() { @@ -847,6 +870,21 @@ public: } } } + + + /// @brief Checks the value of statistic for a given subnet. + /// + /// @param subnet_id Identifier of a subnet for which statistics should be + /// @param name statistic name (e.g. "assigned-addresses", "total-addresses" ...) + /// @param exp_value expected value of the statistic + /// @return Number of assigned addresses for a subnet. + void checkSubnetStat(const SubnetID& subnet_id, const std::string& name, int64_t exp_value) const { + // Retrieve statistics name, e.g. subnet[1234].assigned-addresses. + const std::string stats_name = StatsMgr::generateName("subnet", subnet_id, name); + ObservationPtr obs = StatsMgr::instance().getObservation(stats_name); + ASSERT_TRUE(obs) << "cannot find: " << stats_name; + EXPECT_EQ(exp_value, obs->getInteger().first); + } }; // Tests the following scenario: @@ -2684,4 +2722,108 @@ TEST_F(NameDhcpv4SrvTest, ddnsSharedNetworkTest) { EXPECT_EQ("client2.two.example.com", lease->hostname_); } +// Verifies the basic behavior for a DORA cycle when offer-lifetime is greater +// than zero. +TEST_F(NameDhcpv4SrvTest, withOfferLifetime) { + Dhcp4Client client(Dhcp4Client::SELECTING); + // Use HW address that matches the reservation entry in the configuration. + client.setHWAddress("aa:bb:cc:dd:ee:ff"); + // Configure DHCP server. + configure(CONFIGS[11], *client.getServer()); + + // Fetch the subnet. + Subnet4Ptr subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()-> + selectSubnet(IOAddress("10.0.0.10")); + ASSERT_TRUE(subnet); + + // Make sure that DDNS is enabled. + ASSERT_TRUE(CfgMgr::instance().ddnsEnabled()); + ASSERT_NO_THROW(client.getServer()->startD2()); + // Include the Client FQDN option. + ASSERT_NO_THROW(client.includeFQDN(Option4ClientFqdn::FLAG_S | Option4ClientFqdn::FLAG_E, + "client-name", Option4ClientFqdn::PARTIAL)); + + checkSubnetStat(subnet->getID(), "total-addresses", 1); + checkSubnetStat(subnet->getID(), "cumulative-assigned-addresses", 0); + checkSubnetStat(subnet->getID(), "assigned-addresses", 0); + + // Send the DHCPDISCOVER. + ASSERT_NO_THROW(client.doDiscover()); + + // Make sure that the server responded. + Pkt4Ptr resp = client.getContext().response_; + ASSERT_TRUE(resp); + ASSERT_EQ(DHCPOFFER, static_cast(resp->getType())); + + // Lifetime in the OFFER should be valid lifetime. + OptionUint32Ptr opt = boost::dynamic_pointer_cast< + OptionUint32>(resp->getOption(DHO_DHCP_LEASE_TIME)); + ASSERT_TRUE(opt); + EXPECT_EQ(subnet->getValid(), opt->getValue()); + + // Obtain the FQDN option sent in the response and make sure that the server + // has used the client supplied hostname for this client. + Option4ClientFqdnPtr fqdn; + fqdn = boost::dynamic_pointer_cast(resp->getOption(DHO_FQDN)); + ASSERT_TRUE(fqdn); + EXPECT_EQ("client-name.example.com.", fqdn->getDomainName()); + + // When receiving DHCPDISCOVER, no NCRs should be generated. + EXPECT_EQ(0, d2_mgr_.getQueueSize()); + + // Make sure thed lease was created with offer-lifetime, fqdn flags = false, + // and the FQDN. + Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(IOAddress("10.0.0.10")); + ASSERT_TRUE(lease); + EXPECT_EQ(subnet->getOfferLft(), lease->valid_lft_); + EXPECT_FALSE(lease->fqdn_fwd_); + EXPECT_FALSE(lease->fqdn_rev_); + EXPECT_EQ("client-name.example.com.", lease->hostname_); + + // Verify assigned stats were incremented. + checkSubnetStat(subnet->getID(), "total-addresses", 1); + checkSubnetStat(subnet->getID(), "cumulative-assigned-addresses", 1); + checkSubnetStat(subnet->getID(), "assigned-addresses", 1); + + // Now send the DHCPREQUEST with including the FQDN option. + ASSERT_NO_THROW(client.doRequest()); + resp = client.getContext().response_; + ASSERT_TRUE(resp); + ASSERT_EQ(DHCPACK, static_cast(resp->getType())); + + // Lifetime in the DHCPACK should be valid lifetime. + opt = boost::dynamic_pointer_cast(resp->getOption(DHO_DHCP_LEASE_TIME)); + ASSERT_TRUE(opt); + EXPECT_EQ(subnet->getValid(), opt->getValue()); + + // Once again check that the FQDN is as expected. + fqdn = boost::dynamic_pointer_cast(resp->getOption(DHO_FQDN)); + ASSERT_TRUE(fqdn); + EXPECT_EQ("client-name.example.com.", fqdn->getDomainName()); + + // There should be one NCR which adds the new DNS entry. + ASSERT_EQ(1, CfgMgr::instance().getD2ClientMgr().getQueueSize()); + verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true, + resp->getYiaddr().toText(), + "client-name.example.com.", + "0000011E5D6FA61FCBAC969FF4EF0EBCA3FDE554E" + "B020A13F44859F30A108793564A97", + time(NULL), subnet->getValid(), true); + + + // And that this FQDN has been stored in the lease database. + lease = LeaseMgrFactory::instance().getLease4(client.config_.lease_.addr_); + ASSERT_TRUE(lease); + EXPECT_EQ(subnet->getValid(), lease->valid_lft_); + EXPECT_TRUE(lease->fqdn_fwd_); + EXPECT_TRUE(lease->fqdn_rev_); + EXPECT_EQ("client-name.example.com.", lease->hostname_); + + // Verify assigned states did not incremented again. + checkSubnetStat(subnet->getID(), "total-addresses", 1); + checkSubnetStat(subnet->getID(), "cumulative-assigned-addresses", 1); + checkSubnetStat(subnet->getID(), "assigned-addresses", 1); +} + + } // end of anonymous namespace