diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.cc b/src/bin/dhcp4/ctrl_dhcp4_srv.cc index cddadb086a..f024381593 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.cc +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.cc @@ -71,6 +71,32 @@ ControlledDhcpv4Srv::commandConfigReloadHandler(const string&, return (processConfig(args)); } +ConstElementPtr +ControlledDhcpv4Srv::commandLeasesReclaimHandler(const string& command, + ConstElementPtr args) { + int status_code = 1; + string message; + + // args must be { "remove": } + if (!args) { + message = "Missing mandatory 'remove' parameter."; + } else { + ConstElementPtr remove_name = args->get("remove"); + if (!remove_name) { + message = "Missing mandatory 'remove' parameter."; + } else if (remove_name->getType() != Element::boolean) { + message = "'remove' parameter expected to be a boolean."; + } else { + bool remove_lease = remove_name->boolValue(); + server_->alloc_engine_->reclaimExpiredLeases4(0, 0, remove_lease); + status_code = 0; + message = "Reclamation of expired leases is complete."; + } + } + ConstElementPtr answer = isc::config::createAnswer(status_code, message); + return (answer); +} + ConstElementPtr ControlledDhcpv4Srv::processCommand(const string& command, ConstElementPtr args) { @@ -96,6 +122,8 @@ ControlledDhcpv4Srv::processCommand(const string& command, } else if (command == "config-reload") { return (srv->commandConfigReloadHandler(command, args)); + } else if (command == "leases-reclaim") { + return (srv->commandLeasesReclaimHandler(command, args)); } ConstElementPtr answer = isc::config::createAnswer(1, "Unrecognized command:" + command); @@ -212,6 +240,9 @@ ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t port /*= DHCP4_SERVER_PORT*/) /// @todo: register config-reload (see CtrlDhcpv4Srv::commandConfigReloadHandler) /// @todo: register libreload (see CtrlDhcpv4Srv::commandLibReloadHandler) + CommandMgr::instance().registerCommand("leases-reclaim", + boost::bind(&ControlledDhcpv4Srv::commandLeasesReclaimHandler, this, _1, _2)); + // Register statistic related commands CommandMgr::instance().registerCommand("statistic-get", boost::bind(&StatsMgr::statisticGetHandler, _1, _2)); @@ -251,6 +282,7 @@ ControlledDhcpv4Srv::~ControlledDhcpv4Srv() { // Deregister any registered commands CommandMgr::instance().deregisterCommand("shutdown"); + CommandMgr::instance().deregisterCommand("leases-reclaim"); CommandMgr::instance().deregisterCommand("statistic-get"); CommandMgr::instance().deregisterCommand("statistic-reset"); CommandMgr::instance().deregisterCommand("statistic-remove"); diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.h b/src/bin/dhcp4/ctrl_dhcp4_srv.h index 1cd7f78561..d1abf7e450 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.h +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.h @@ -70,6 +70,7 @@ public: /// - shutdown /// - libreload /// - config-reload + /// - leases-reclaim /// /// @note It never throws. /// @@ -151,6 +152,23 @@ private: isc::data::ConstElementPtr args); + /// @brief Handler for processing 'leases-reclaim' command + /// + /// This handler processes leases-reclaim command, which triggers + /// the leases reclamation immediately. + /// No limit for processing time or number of processed leases applies. + /// + /// @param command (parameter ignored) + /// @param args arguments map { "remove": } + /// if true a lease is removed when it is reclaimed, + /// if false its state is changed to "expired-reclaimed". + /// + /// @return status of the command (should be success unless args + /// was not a Bool Element). + isc::data::ConstElementPtr + commandLeasesReclaimHandler(const std::string& command, + isc::data::ConstElementPtr args); + /// @brief Reclaims expired IPv4 leases and reschedules timer. /// /// This is a wrapper method for @c AllocEngine::reclaimExpiredLeases4. diff --git a/src/bin/dhcp4/kea_controller.cc b/src/bin/dhcp4/kea_controller.cc index 92503d5cf0..d77bb37756 100644 --- a/src/bin/dhcp4/kea_controller.cc +++ b/src/bin/dhcp4/kea_controller.cc @@ -69,11 +69,11 @@ void configure(const std::string& file_name) { // works only for map. if (json->getType() != isc::data::Element::map) { isc_throw(isc::BadValue, "Configuration file is expected to be " - "a map, i.e., start with { and end with } and contain " - "at least an entry called 'Dhcp4' that itself is a map. " - << file_name + "a map, i.e., start with { and end with } and contain " + "at least an entry called 'Dhcp4' that itself is a map. " + << file_name << " is a valid JSON, but its top element is not a map." - " Did you forget to add { } around your configuration?"); + " Did you forget to add { } around your configuration?"); } // If there's no logging element, we'll just pass NULL pointer, diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc index 933146facc..d89c8a2ba4 100644 --- a/src/bin/dhcp4/tests/config_parser_unittest.cc +++ b/src/bin/dhcp4/tests/config_parser_unittest.cc @@ -3665,7 +3665,7 @@ TEST_F(Dhcp4ParserTest, declineTimerDefault) { // returned value should be 0 (success) checkResult(status, 0); - // The value of decline-probation-perion must be equal to the + // The value of decline-probation-period must be equal to the // default value. EXPECT_EQ(DEFAULT_DECLINE_PROBATION_PERIOD, CfgMgr::instance().getStagingCfg()->getDeclinePeriod()); @@ -3687,7 +3687,7 @@ TEST_F(Dhcp4ParserTest, declineTimer) { // returned value should be 0 (success) checkResult(status, 0); - // The value of decline-probation-perion must be equal to the + // The value of decline-probation-period must be equal to the // value specified. EXPECT_EQ(12345, CfgMgr::instance().getStagingCfg()->getDeclinePeriod()); @@ -3738,7 +3738,7 @@ TEST_F(Dhcp4ParserTest, expiredLeasesProcessing) { // Returned value should be 0 (success) checkResult(status, 0); - // The value of decline-probation-perion must be equal to the + // The value of decline-probation-period must be equal to the // value specified. CfgExpirationPtr cfg = CfgMgr::instance().getStagingCfg()->getCfgExpiration(); ASSERT_TRUE(cfg); diff --git a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc index af2f5e676a..9cf01e236e 100644 --- a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2012-2013, 2015 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -19,7 +19,10 @@ #include #include #include +#include +#include #include +#include #include #include "marker_file.h" @@ -43,6 +46,7 @@ using namespace isc::data; using namespace isc::dhcp; using namespace isc::dhcp::test; using namespace isc::hooks; +using namespace isc::stats; namespace { @@ -80,6 +84,8 @@ public: /// @brief Destructor ~CtrlChannelDhcpv4SrvTest() { + LeaseMgrFactory::destroy(); + StatsMgr::instance().removeAll(); server_.reset(); reset(); }; @@ -94,6 +100,11 @@ public: " \"interfaces-config\": {" " \"interfaces\": [ \"*\" ]" " }," + " \"expired-leases-processing\": {" + " \"reclaim-timer-wait-time\": 60," + " \"hold-reclaimed-time\": 500," + " \"flush-reclaimed-timer-wait-time\": 60" + " }," " \"rebind-timer\": 2000, " " \"renew-timer\": 1000, " " \"subnet4\": [ ]," @@ -333,6 +344,103 @@ TEST_F(CtrlChannelDhcpv4SrvTest, controlChannelShutdown) { EXPECT_EQ("{ \"result\": 0, \"text\": \"Shutting down.\" }",response); } +// This test verifies that the DHCP server immediately reclaims expired +// leases on leases-reclaim command +TEST_F(CtrlChannelDhcpv4SrvTest, controlLeasesReclaim) { + createUnixChannelServer(); + + // Create expired leases. Leases are expired by 40 seconds ago + // (valid lifetime = 60, cltt = now - 100). + HWAddrPtr hwaddr0(new HWAddr(HWAddr::fromText("00:01:02:03:04:05"))); + Lease4Ptr lease0(new Lease4(IOAddress("10.0.0.1"), hwaddr0, + ClientIdPtr(), 60, 10, 20, + time(NULL) - 100, SubnetID(1))); + HWAddrPtr hwaddr1(new HWAddr(HWAddr::fromText("01:02:03:04:05:06"))); + Lease4Ptr lease1(new Lease4(IOAddress("10.0.0.2"), hwaddr1, + ClientIdPtr(), 60, 10, 20, + time(NULL) - 100, SubnetID(1))); + + // Add leases to the database. + LeaseMgr& lease_mgr = LeaseMgrFactory().instance(); + ASSERT_NO_THROW(lease_mgr.addLease(lease0)); + ASSERT_NO_THROW(lease_mgr.addLease(lease1)); + + // Make sure they have been added. + ASSERT_TRUE(lease_mgr.getLease4(IOAddress("10.0.0.1"))); + ASSERT_TRUE(lease_mgr.getLease4(IOAddress("10.0.0.2"))); + + // No arguments + std::string response; + sendUnixCommand("{ \"command\": \"leases-reclaim\" }", response); + EXPECT_EQ("{ \"result\": 1, \"text\": " + "\"Missing mandatory 'remove' parameter.\" }", response); + + // Bad argument name + sendUnixCommand("{ \"command\": \"leases-reclaim\", " + "\"arguments\": { \"reclaim\": true } }", response); + EXPECT_EQ("{ \"result\": 1, \"text\": " + "\"Missing mandatory 'remove' parameter.\" }", response); + + // Bad remove argument type + sendUnixCommand("{ \"command\": \"leases-reclaim\", " + "\"arguments\": { \"remove\": \"bogus\" } }", response); + EXPECT_EQ("{ \"result\": 1, \"text\": " + "\"'remove' parameter expected to be a boolean.\" }", response); + + // Send the command + sendUnixCommand("{ \"command\": \"leases-reclaim\", " + "\"arguments\": { \"remove\": false } }", response); + EXPECT_EQ("{ \"result\": 0, \"text\": " + "\"Reclamation of expired leases is complete.\" }", response); + + // Leases should be reclaimed, but not removed + ASSERT_NO_THROW(lease0 = lease_mgr.getLease4(IOAddress("10.0.0.1"))); + ASSERT_NO_THROW(lease1 = lease_mgr.getLease4(IOAddress("10.0.0.2"))); + ASSERT_TRUE(lease0); + ASSERT_TRUE(lease1); + EXPECT_TRUE(lease0->stateExpiredReclaimed()); + EXPECT_TRUE(lease1->stateExpiredReclaimed()); +} + +// Thist test verifies that the DHCP server immediately removed expired +// leases on leases-reclaim command with remove = true +TEST_F(CtrlChannelDhcpv4SrvTest, controlLeasesReclaimRemove) { + createUnixChannelServer(); + + // Create expired leases. Leases are expired by 40 seconds ago + // (valid lifetime = 60, cltt = now - 100). + HWAddrPtr hwaddr0(new HWAddr(HWAddr::fromText("00:01:02:03:04:05"))); + Lease4Ptr lease0(new Lease4(IOAddress("10.0.0.1"), hwaddr0, + ClientIdPtr(), 60, 10, 20, + time(NULL) - 100, SubnetID(1))); + HWAddrPtr hwaddr1(new HWAddr(HWAddr::fromText("01:02:03:04:05:06"))); + Lease4Ptr lease1(new Lease4(IOAddress("10.0.0.2"), hwaddr1, + ClientIdPtr(), 60, 10, 20, + time(NULL) - 100, SubnetID(1))); + + // Add leases to the database. + LeaseMgr& lease_mgr = LeaseMgrFactory().instance(); + ASSERT_NO_THROW(lease_mgr.addLease(lease0)); + ASSERT_NO_THROW(lease_mgr.addLease(lease1)); + + // Make sure they have been added. + ASSERT_TRUE(lease_mgr.getLease4(IOAddress("10.0.0.1"))); + ASSERT_TRUE(lease_mgr.getLease4(IOAddress("10.0.0.2"))); + + // Send the command + std::string response; + sendUnixCommand("{ \"command\": \"leases-reclaim\", " + "\"arguments\": { \"remove\": true } }", response); + EXPECT_EQ("{ \"result\": 0, \"text\": " + "\"Reclamation of expired leases is complete.\" }", response); + + // Leases should have been removed. + ASSERT_NO_THROW(lease0 = lease_mgr.getLease4(IOAddress("10.0.0.1"))); + ASSERT_NO_THROW(lease1 = lease_mgr.getLease4(IOAddress("10.0.0.2"))); + EXPECT_FALSE(lease0); + EXPECT_FALSE(lease1); +} + // Tests that the server properly responds to statistics commands. Note this // is really only intended to verify that the appropriate Statistics handler // is called based on the command. It is not intended to be an exhaustive @@ -379,5 +487,4 @@ TEST_F(CtrlChannelDhcpv4SrvTest, controlChannelStats) { response); } - } // End of anonymous namespace diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc index 91f983cf6a..b1fa5e4cfc 100644 --- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc +++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc @@ -69,6 +69,32 @@ ControlledDhcpv6Srv::commandConfigReloadHandler(const string&, ConstElementPtr a return (processConfig(args)); } +ConstElementPtr +ControlledDhcpv6Srv::commandLeasesReclaimHandler(const string& command, + ConstElementPtr args) { + int status_code = 1; + string message; + + // args must be { "remove": } + if (!args) { + message = "Missing mandatory 'remove' parameter."; + } else { + ConstElementPtr remove_name = args->get("remove"); + if (!remove_name) { + message = "Missing mandatory 'remove' parameter."; + } else if (remove_name->getType() != Element::boolean) { + message = "'remove' parameter expected to be a boolean."; + } else { + bool remove_lease = remove_name->boolValue(); + server_->alloc_engine_->reclaimExpiredLeases6(0, 0, remove_lease); + status_code = 0; + message = "Reclamation of expired leases is complete."; + } + } + ConstElementPtr answer = isc::config::createAnswer(status_code, message); + return (answer); +} + isc::data::ConstElementPtr ControlledDhcpv6Srv::processCommand(const std::string& command, isc::data::ConstElementPtr args) { @@ -93,6 +119,9 @@ ControlledDhcpv6Srv::processCommand(const std::string& command, } else if (command == "config-reload") { return (srv->commandConfigReloadHandler(command, args)); + + } else if (command == "leases-reclaim") { + return (srv->commandLeasesReclaimHandler(command, args)); } return (isc::config::createAnswer(1, "Unrecognized command:" @@ -208,6 +237,9 @@ ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t port) /// @todo: register config-reload (see CtrlDhcpv4Srv::commandConfigReloadHandler) /// @todo: register libreload (see CtrlDhcpv4Srv::commandLibReloadHandler) + CommandMgr::instance().registerCommand("leases-reclaim", + boost::bind(&ControlledDhcpv6Srv::commandLeasesReclaimHandler, this, _1, _2)); + // Register statistic related commands CommandMgr::instance().registerCommand("statistic-get", boost::bind(&StatsMgr::statisticGetHandler, _1, _2)); @@ -247,6 +279,7 @@ ControlledDhcpv6Srv::~ControlledDhcpv6Srv() { // Deregister any registered commands CommandMgr::instance().deregisterCommand("shutdown"); + CommandMgr::instance().deregisterCommand("leases-reclaim"); CommandMgr::instance().deregisterCommand("statistic-get"); CommandMgr::instance().deregisterCommand("statistic-reset"); CommandMgr::instance().deregisterCommand("statistic-remove"); diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.h b/src/bin/dhcp6/ctrl_dhcp6_srv.h index 07c411c10c..cc58155192 100644 --- a/src/bin/dhcp6/ctrl_dhcp6_srv.h +++ b/src/bin/dhcp6/ctrl_dhcp6_srv.h @@ -70,6 +70,7 @@ public: /// - shutdown /// - libreload /// - config-reload + /// - leases-reclaim /// /// @note It never throws. /// @@ -150,6 +151,23 @@ private: commandConfigReloadHandler(const std::string& command, isc::data::ConstElementPtr args); + /// @brief Handler for processing 'leases-reclaim' command + /// + /// This handler processes leases-reclaim command, which triggers + /// the leases reclamation immediately. + /// No limit for processing time or number of processed leases applies. + /// + /// @param command (parameter ignored) + /// @param args arguments map { "remove": } + /// if true a lease is removed when it is reclaimed, + /// if false its state is changed to "expired-reclaimed". + /// + /// @return status of the command (should be success unless args + /// was not a Bool Element). + isc::data::ConstElementPtr + commandLeasesReclaimHandler(const std::string& command, + isc::data::ConstElementPtr args); + /// @brief Reclaims expired IPv6 leases and reschedules timer. /// /// This is a wrapper method for @c AllocEngine::reclaimExpiredLeases6. diff --git a/src/bin/dhcp6/kea_controller.cc b/src/bin/dhcp6/kea_controller.cc index f1fc901f97..2962f0eb15 100644 --- a/src/bin/dhcp6/kea_controller.cc +++ b/src/bin/dhcp6/kea_controller.cc @@ -73,11 +73,11 @@ void configure(const std::string& file_name) { // works only for map. if (json->getType() != isc::data::Element::map) { isc_throw(isc::BadValue, "Configuration file is expected to be " - "a map, i.e., start with { and end with } and contain " - "at least an entry called 'Dhcp6' that itself is a map. " - << file_name + "a map, i.e., start with { and end with } and contain " + "at least an entry called 'Dhcp6' that itself is a map. " + << file_name << " is a valid JSON, but its top element is not a map." - " Did you forget to add { } around your configuration?"); + " Did you forget to add { } around your configuration?"); } // Let's configure logging before applying the configuration, diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc index f2e945191a..108902698e 100644 --- a/src/bin/dhcp6/tests/config_parser_unittest.cc +++ b/src/bin/dhcp6/tests/config_parser_unittest.cc @@ -3999,7 +3999,7 @@ TEST_F(Dhcp6ParserTest, declineTimerDefault) { // returned value should be 0 (success) checkResult(status, 0); - // The value of decline-probation-perion must be equal to the + // The value of decline-probation-period must be equal to the // default value. EXPECT_EQ(DEFAULT_DECLINE_PROBATION_PERIOD, CfgMgr::instance().getStagingCfg()->getDeclinePeriod()); @@ -4021,7 +4021,7 @@ TEST_F(Dhcp6ParserTest, declineTimer) { // returned value should be 0 (success) checkResult(status, 0); - // The value of decline-probation-perion must be equal to the + // The value of decline-probation-period must be equal to the // value specified. EXPECT_EQ(12345, CfgMgr::instance().getStagingCfg()->getDeclinePeriod()); @@ -4072,7 +4072,7 @@ TEST_F(Dhcp6ParserTest, expiredLeasesProcessing) { // Returned value should be 0 (success) checkResult(status, 0); - // The value of decline-probation-perion must be equal to the + // The value of decline-probation-period must be equal to the // value specified. CfgExpirationPtr cfg = CfgMgr::instance().getStagingCfg()->getCfgExpiration(); ASSERT_TRUE(cfg); diff --git a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc index cab9f19408..967bdb2067 100644 --- a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc @@ -14,11 +14,15 @@ #include +#include #include #include #include +#include +#include #include #include +#include #include #include "marker_file.h" @@ -32,11 +36,13 @@ #include using namespace std; +using namespace isc::asiolink; using namespace isc::config; using namespace isc::data; using namespace isc::dhcp; using namespace isc::dhcp::test; using namespace isc::hooks; +using namespace isc::stats; namespace { @@ -59,6 +65,8 @@ public: } virtual ~CtrlDhcpv6SrvTest() { + LeaseMgrFactory::destroy(); + StatsMgr::instance().removeAll(); reset(); }; @@ -118,6 +126,11 @@ public: " \"interfaces-config\": {" " \"interfaces\": [ \"*\" ]" " }," + " \"expired-leases-processing\": {" + " \"reclaim-timer-wait-time\": 60," + " \"hold-reclaimed-time\": 500," + " \"flush-reclaimed-timer-wait-time\": 60" + " }," " \"rebind-timer\": 2000, " " \"renew-timer\": 1000, " " \"subnet6\": [ ]," @@ -406,6 +419,111 @@ TEST_F(CtrlChannelDhcpv6SrvTest, controlChannelShutdown) { EXPECT_EQ("{ \"result\": 0, \"text\": \"Shutting down.\" }",response); } +// This test verifies that the DHCP server immediately reclaims expired +// leases on leases-reclaim command +TEST_F(CtrlChannelDhcpv6SrvTest, controlLeasesReclaim) { + createUnixChannelServer(); + + // Create expired leases. Leases are expired by 40 seconds ago + // (valid lifetime = 60, cltt = now - 100). + DuidPtr duid0(new DUID(DUID::fromText("00:01:02:03:04:05:06").getDuid())); + Lease6Ptr lease0(new Lease6(Lease::TYPE_NA, IOAddress("3000::1"), + duid0, 1, 50, 60, 10, 20, SubnetID(1))); + lease0->cltt_ = time(NULL) - 100; + DuidPtr duid1(new DUID(DUID::fromText("01:02:03:04:05:06:07").getDuid())); + Lease6Ptr lease1(new Lease6(Lease::TYPE_NA, IOAddress("3000::2"), + duid1, 1, 50, 60, 10, 20, SubnetID(1))); + lease1->cltt_ = time(NULL) - 100; + + // Add leases to the database. + LeaseMgr& lease_mgr = LeaseMgrFactory().instance(); + ASSERT_NO_THROW(lease_mgr.addLease(lease0)); + ASSERT_NO_THROW(lease_mgr.addLease(lease1)); + + // Make sure they have been added. + ASSERT_TRUE(lease_mgr.getLease6(Lease::TYPE_NA, IOAddress("3000::1"))); + ASSERT_TRUE(lease_mgr.getLease6(Lease::TYPE_NA, IOAddress("3000::2"))); + + // No arguments + std::string response; + sendUnixCommand("{ \"command\": \"leases-reclaim\" }", response); + EXPECT_EQ("{ \"result\": 1, \"text\": " + "\"Missing mandatory 'remove' parameter.\" }", response); + + // Bad argument name + sendUnixCommand("{ \"command\": \"leases-reclaim\", " + "\"arguments\": { \"reclaim\": true } }", response); + EXPECT_EQ("{ \"result\": 1, \"text\": " + "\"Missing mandatory 'remove' parameter.\" }", response); + + // Bad remove argument type + sendUnixCommand("{ \"command\": \"leases-reclaim\", " + "\"arguments\": { \"remove\": \"bogus\" } }", response); + EXPECT_EQ("{ \"result\": 1, \"text\": " + "\"'remove' parameter expected to be a boolean.\" }", response); + + // Send the command + sendUnixCommand("{ \"command\": \"leases-reclaim\", " + "\"arguments\": { \"remove\": false } }", response); + EXPECT_EQ("{ \"result\": 0, \"text\": " + "\"Reclamation of expired leases is complete.\" }", response); + + // Leases should be reclaimed, but not removed + ASSERT_NO_THROW( + lease0 = lease_mgr.getLease6(Lease::TYPE_NA, IOAddress("3000::1")) + ); + ASSERT_NO_THROW( + lease1 = lease_mgr.getLease6(Lease::TYPE_NA, IOAddress("3000::2")) + ); + ASSERT_TRUE(lease0); + ASSERT_TRUE(lease1); + EXPECT_TRUE(lease0->stateExpiredReclaimed()); + EXPECT_TRUE(lease1->stateExpiredReclaimed()); +} + +// This test verifies that the DHCP server immediately reclaims expired +// leases on leases-reclaim command with remove = true +TEST_F(CtrlChannelDhcpv6SrvTest, controlLeasesReclaimRemove) { + createUnixChannelServer(); + + // Create expired leases. Leases are expired by 40 seconds ago + // (valid lifetime = 60, cltt = now - 100). + DuidPtr duid0(new DUID(DUID::fromText("00:01:02:03:04:05:06").getDuid())); + Lease6Ptr lease0(new Lease6(Lease::TYPE_NA, IOAddress("3000::1"), + duid0, 1, 50, 60, 10, 20, SubnetID(1))); + lease0->cltt_ = time(NULL) - 100; + DuidPtr duid1(new DUID(DUID::fromText("01:02:03:04:05:06:07").getDuid())); + Lease6Ptr lease1(new Lease6(Lease::TYPE_NA, IOAddress("3000::2"), + duid1, 1, 50, 60, 10, 20, SubnetID(1))); + lease1->cltt_ = time(NULL) - 100; + + // Add leases to the database. + LeaseMgr& lease_mgr = LeaseMgrFactory().instance(); + ASSERT_NO_THROW(lease_mgr.addLease(lease0)); + ASSERT_NO_THROW(lease_mgr.addLease(lease1)); + + // Make sure they have been added. + ASSERT_TRUE(lease_mgr.getLease6(Lease::TYPE_NA, IOAddress("3000::1"))); + ASSERT_TRUE(lease_mgr.getLease6(Lease::TYPE_NA, IOAddress("3000::2"))); + + // Send the command + std::string response; + sendUnixCommand("{ \"command\": \"leases-reclaim\", " + "\"arguments\": { \"remove\": true } }", response); + EXPECT_EQ("{ \"result\": 0, \"text\": " + "\"Reclamation of expired leases is complete.\" }", response); + + // Leases should have been removed. + ASSERT_NO_THROW( + lease0 = lease_mgr.getLease6(Lease::TYPE_NA, IOAddress("3000::1")) + ); + ASSERT_NO_THROW( + lease1 = lease_mgr.getLease6(Lease::TYPE_NA, IOAddress("3000::2")) + ); + ASSERT_FALSE(lease0); + ASSERT_FALSE(lease1); +} + // Tests that the server properly responds to statistics commands. Note this // is really only intended to verify that the appropriate Statistics handler // is called based on the command. It is not intended to be an exhaustive diff --git a/src/lib/config/command_mgr.h b/src/lib/config/command_mgr.h index ba4f276176..546afdc0a0 100644 --- a/src/lib/config/command_mgr.h +++ b/src/lib/config/command_mgr.h @@ -65,7 +65,7 @@ public: /// } /// } /// -/// CommandsMgr does not implement the commands (except one, "commands-list") +/// CommandsMgr does not implement the commands (except one, "list-commands") /// itself, but rather provides an interface (see @ref registerCommand, /// @ref deregisterCommand, @ref processCommand) for other components to use /// it. The @ref CommandHandler type is specified in a way to easily use