diff options
author | Marcin Siodelski <marcin@isc.org> | 2018-06-26 21:36:43 +0200 |
---|---|---|
committer | Marcin Siodelski <marcin@isc.org> | 2018-06-26 21:36:43 +0200 |
commit | 2e632454da934fd674b353dccb664804bde9d88f (patch) | |
tree | 6f5db0bedbdd67641b86b2d9e9e0cfa8ef3c571b | |
parent | [5651] Guard against invalid address family. (diff) | |
download | kea-2e632454da934fd674b353dccb664804bde9d88f.tar.xz kea-2e632454da934fd674b353dccb664804bde9d88f.zip |
[5651] Implemented leaseX-get-page commands.
-rw-r--r-- | src/hooks/dhcp/lease_cmds/lease_cmds.cc | 160 | ||||
-rw-r--r-- | src/hooks/dhcp/lease_cmds/lease_cmds.h | 17 | ||||
-rw-r--r-- | src/hooks/dhcp/lease_cmds/lease_cmds_callouts.cc | 26 | ||||
-rw-r--r-- | src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc | 377 |
4 files changed, 574 insertions, 6 deletions
diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds.cc b/src/hooks/dhcp/lease_cmds/lease_cmds.cc index 19f0c4794a..955e75d6da 100644 --- a/src/hooks/dhcp/lease_cmds/lease_cmds.cc +++ b/src/hooks/dhcp/lease_cmds/lease_cmds.cc @@ -24,6 +24,7 @@ #include <util/strutil.h> #include <boost/bind.hpp> +#include <boost/scoped_ptr.hpp> #include <string> #include <sstream> @@ -143,6 +144,23 @@ public: int leaseGetAllHandler(CalloutHandle& handle); + /// @brief lease4-get-page, lease6-get-page commands handler + /// + /// These commands attempt to retrieve 1 page of all IPv4 or IPv6 + /// leases. The size of the page is specified by the caller. The + /// caller also specifies the last address returned in the previous + /// page. The new page starts from the first address following the + /// address specified by the caller. If the first page should be + /// returned the IPv4 zero address, IPv6 zero address or the keyword + /// "begin" should be provided instead of the last address. + /// + /// @param handle Callout context - which is expected to contain the + /// get commands JSON text in the "command" argument. + /// @return 0 if the handler has been invoked successfully, 1 if an + /// error occurs, 3 if no leases are returned. + int + leaseGetPageHandler(hooks::CalloutHandle& handle); + /// @brief lease4-del command handler /// /// Provides the implementation for @ref isc::lease_cmds::LeaseCmds::lease4DelHandler @@ -555,6 +573,143 @@ LeaseCmdsImpl::leaseGetAllHandler(CalloutHandle& handle) { } int +LeaseCmdsImpl::leaseGetPageHandler(CalloutHandle& handle) { + bool v4 = true; + try { + extractCommand(handle); + v4 = (cmd_name_ == "lease4-get-page"); + + // arguments must always be present + if (!cmd_args_) { + isc_throw(BadValue, "no parameters specified for the " << cmd_name_ + << " command"); + } + + // The 'from' argument denotes from which lease we should start the + // results page. The results page excludes this lease. + ConstElementPtr from = cmd_args_->get("from"); + if (!from) { + isc_throw(BadValue, "'from' parameter not specified"); + } + + // The 'from' argument is a string. It may contain a 'start' keyword or + // an IP address. + if (from->getType() != Element::string) { + isc_throw(BadValue, "'from' parameter must be a string"); + } + + boost::scoped_ptr<IOAddress> from_address; + try { + if (from->stringValue() == "start") { + from_address.reset(new IOAddress(v4 ? "0.0.0.0" : "::")); + + } else { + // Conversion of a string to an IP address may throw. + from_address.reset(new IOAddress(from->stringValue())); + } + + } catch (...) { + isc_throw(BadValue, "'from' parameter value is neither 'start' keyword nor " + "a valid IPv" << (v4 ? "4" : "6") << " address"); + } + + // It must be either IPv4 address for lease4-get-page or IPv6 address for + // lease6-get-page. + if (v4 && (!from_address->isV4())) { + isc_throw(BadValue, "'from' parameter value " << from_address->toText() + << " is not an IPv4 address"); + + } else if (!v4 && from_address->isV4()) { + isc_throw(BadValue, "'from' parameter value " << from_address->toText() + << " is not an IPv6 address"); + } + + // The 'count' is a desired page size. It must always be present. + ConstElementPtr page_count = cmd_args_->get("count"); + if (!page_count) { + isc_throw(BadValue, "'count' parameter not specified"); + } + + // The 'count' must be a number. + if (page_count->getType() != Element::integer) { + isc_throw(BadValue, "'count' parameter must be a number"); + } + + // Retrieve the desired page size. + size_t page_count_value = static_cast<size_t>(page_count->intValue()); + + ElementPtr leases_json = Element::createList(); + + // Use lease stats function to retrieve the total number of leases. + // Total number of leases is returned apart from the leases list + // so as the controlling client can track the progress of leases + // viewed vs all leases count. + LeaseStatsQueryPtr query; + if (v4) { + // Get page of IPv4 leases. + Lease4Collection leases = + LeaseMgrFactory::instance().getLeases4(*from_address, + LeasePageSize(page_count_value)); + // Get the total lease count. + query = LeaseMgrFactory::instance().startLeaseStatsQuery4(); + + // Convert leases into JSON list. + for (auto lease : leases) { + ElementPtr lease_json = lease->toElement(); + leases_json->add(lease_json); + } + + } else { + // Get page of IPv6 leases. + Lease6Collection leases = + LeaseMgrFactory::instance().getLeases6(*from_address, + LeasePageSize(page_count_value)); + // Get the total lease count. + query = LeaseMgrFactory::instance().startLeaseStatsQuery6(); + + // Convert leases into JSON list. + for (auto lease : leases) { + ElementPtr lease_json = lease->toElement(); + leases_json->add(lease_json); + } + } + + // Sum up lease counters for various lease states. + LeaseStatsRow row; + int64_t total_leases = 0; + while (query->getNextRow(row)) { + total_leases += row.state_count_; + } + + // Prepare textual status. + std::ostringstream s; + s << leases_json->size() + << " IPv" << (v4 ? "4" : "6") + << " lease(s) found."; + ElementPtr args = Element::createMap(); + + // Put gathered data into arguments map. + args->set("leases", leases_json); + args->set("count", Element::create(static_cast<int64_t>(leases_json->size()))); + args->set("total-count", Element::create(total_leases)); + + // Create the response. + ConstElementPtr response = + createAnswer(leases_json->size() > 0 ? + CONTROL_RESULT_SUCCESS : + CONTROL_RESULT_EMPTY, + s.str(), args); + setResponse(handle, response); + + } catch (std::exception& ex) { + setErrorResponse(handle, ex.what()); + return (CONTROL_RESULT_ERROR); + } + + return (CONTROL_RESULT_SUCCESS); +} + +int LeaseCmdsImpl::lease4DelHandler(CalloutHandle& handle) { Parameters p; Lease4Ptr lease4; @@ -868,6 +1023,11 @@ LeaseCmds::leaseGetAllHandler(hooks::CalloutHandle& handle) { } int +LeaseCmds::leaseGetPageHandler(hooks::CalloutHandle& handle) { + return (impl_->leaseGetPageHandler(handle)); +} + +int LeaseCmds::lease4DelHandler(CalloutHandle& handle) { return(impl_->lease4DelHandler(handle)); } diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds.h b/src/hooks/dhcp/lease_cmds/lease_cmds.h index 7b8fd123a3..ff842480f1 100644 --- a/src/hooks/dhcp/lease_cmds/lease_cmds.h +++ b/src/hooks/dhcp/lease_cmds/lease_cmds.h @@ -159,6 +159,23 @@ public: int leaseGetAllHandler(hooks::CalloutHandle& handle); + /// @brief lease4-get-page, lease6-get-page commands handler + /// + /// These commands attempt to retrieve 1 page of all IPv4 or IPv6 + /// leases. The size of the page is specified by the caller. The + /// caller also specifies the last address returned in the previous + /// page. The new page starts from the first address following the + /// address specified by the caller. If the first page should be + /// returned the IPv4 zero address, IPv6 zero address or the keyword + /// "start" should be provided instead of the last address. + /// + /// @param handle Callout context - which is expected to contain the + /// get commands JSON text in the "command" argument. + /// @return 0 if the handler has been invoked successfully, 1 if an + /// error occurs, 3 if no leases are returned. + int + leaseGetPageHandler(hooks::CalloutHandle& handle); + /// @brief lease4-del command handler /// /// This command attempts to delete an IPv4 lease that match selected diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds_callouts.cc b/src/hooks/dhcp/lease_cmds/lease_cmds_callouts.cc index 97912ce71e..12115941e7 100644 --- a/src/hooks/dhcp/lease_cmds/lease_cmds_callouts.cc +++ b/src/hooks/dhcp/lease_cmds/lease_cmds_callouts.cc @@ -88,6 +88,28 @@ int lease6_get_all(CalloutHandle& handle) { return (lease_cmds.leaseGetAllHandler(handle)); } +/// @brief This is a command callout for 'lease4-get-page' command. +/// +/// @param handle Callout handle used to retrieve a command and +/// provide a response. +/// @return 0 if this callout has been invoked successfully, +/// 1 if an error occurs, 3 if no leases are returned. +int lease4_get_page(CalloutHandle& handle) { + LeaseCmds lease_cmds; + return (lease_cmds.leaseGetPageHandler(handle)); +} + +/// @brief This is a command callout for 'lease6-get-page' command. +/// +/// @param handle Callout handle used to retrieve a command and +/// provide a response. +/// @return 0 if this callout has been invoked successfully, +/// 1 if an error occurs, 3 if no leases are returned. +int lease6_get_page(CalloutHandle& handle) { + LeaseCmds lease_cmds; + return (lease_cmds.leaseGetPageHandler(handle)); +} + /// @brief This is a command callout for 'lease4-del' command. /// /// @param handle Callout handle used to retrieve a command and @@ -162,9 +184,11 @@ int load(LibraryHandle& handle) { handle.registerCommandCallout("lease4-add", lease4_add); handle.registerCommandCallout("lease6-add", lease6_add); handle.registerCommandCallout("lease4-get", lease4_get); - handle.registerCommandCallout("lease4-get-all", lease4_get_all); handle.registerCommandCallout("lease6-get", lease6_get); + handle.registerCommandCallout("lease4-get-all", lease4_get_all); handle.registerCommandCallout("lease6-get-all", lease6_get_all); + handle.registerCommandCallout("lease4-get-page", lease4_get_page); + handle.registerCommandCallout("lease6-get-page", lease6_get_page); handle.registerCommandCallout("lease4-del", lease4_del); handle.registerCommandCallout("lease6-del", lease6_del); handle.registerCommandCallout("lease4-update", lease4_update); diff --git a/src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc b/src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc index 04b3252695..315a86f1e5 100644 --- a/src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc +++ b/src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc @@ -17,6 +17,7 @@ #include <gtest/gtest.h> #include <cc/data.h> #include <errno.h> +#include <set> using namespace std; using namespace isc; @@ -477,11 +478,13 @@ public: // Simple test that checks the library really registers the commands. TEST_F(LeaseCmdsTest, commands) { - vector<string> cmds = { "lease4-add", "lease6-add", - "lease4-get", "lease6-get", - "lease4-del", "lease6-del", - "lease4-update", "lease6-update", - "lease4-wipe", "lease6-wipe" }; + vector<string> cmds = { "lease4-add", "lease6-add", + "lease4-get", "lease6-get", + "lease4-get-all", "lease6-get-all", + "lease4-get-page", "lease6-get-page", + "lease4-del", "lease6-del", + "lease4-update", "lease6-update", + "lease4-wipe", "lease6-wipe" }; testCommands(cmds); } @@ -1589,6 +1592,147 @@ TEST_F(LeaseCmdsTest, Lease4GetBySubnetIdInvalidArguments) { testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); } +// Checks that multiple calls to lease4-get-pages return all leases. +TEST_F(LeaseCmdsTest, Lease4GetPaged) { + + // Initialize lease manager (false = v4, true = add a lease) + initLeaseMgr(false, true); + + // Gather all returned addresses to verify that all were returned. + std::set<std::string> lease_addresses; + + // Keyword start indicates that we want to retrieve the first page. + std::string last_address = "start"; + + // There are 4 leases in the database, so the first two pages should + // include leases and the 3 page should be empty. + for (auto i = 0; i < 3; ++i) { + // Query for a page of leases. + string cmd = + "{\n" + " \"command\": \"lease4-get-page\",\n" + " \"arguments\": {" + " \"from\": \"" + last_address + "\"," + " \"count\": 2" + " }" + "}"; + + // For the first two pages we shuould get success. For the last + // one an empty status code. + ConstElementPtr rsp; + if (i < 2) { + string exp_rsp = "2 IPv4 lease(s) found."; + rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp); + + } else { + string exp_rsp = "0 IPv4 lease(s) found."; + rsp = testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp); + + } + + // Now check that the lease parameters were indeed returned. + ASSERT_TRUE(rsp); + + // Arguments must exist. + ConstElementPtr args = rsp->get("arguments"); + ASSERT_TRUE(args); + ASSERT_EQ(Element::map, args->getType()); + + // Each response must include total number of leases to allow the + // controlling client to track progress (e.g. percentage of leases + // already viewed). + ConstElementPtr total_count = args->get("total-count"); + ASSERT_TRUE(total_count); + ASSERT_EQ(Element::integer, total_count->getType()); + EXPECT_EQ(4, total_count->intValue()); + + // For convenience, we also return the number of returned leases, + // so as the client can check whether there was anything returned + // before parsing the leases structure. + ConstElementPtr page_count = args->get("count"); + ASSERT_TRUE(page_count); + ASSERT_EQ(Element::integer, page_count->getType()); + + // leases must exist, but may be empty. + ConstElementPtr leases = args->get("leases"); + ASSERT_TRUE(leases); + ASSERT_EQ(Element::list, leases->getType()); + + if (!leases->empty()) { + EXPECT_EQ(2, page_count->intValue()); + + // Go over each lease and verify its correctness. + for (ConstElementPtr lease : leases->listValue()) { + ASSERT_EQ(Element::map, lease->getType()); + ASSERT_TRUE(lease->contains("ip-address")); + ConstElementPtr ip_address = lease->get("ip-address"); + ASSERT_EQ(Element::string, ip_address->getType()); + last_address = ip_address->stringValue(); + + lease_addresses.insert(last_address); + + // The easiest way to retrieve the subnet id and HW address is to + // ask the Lease Manager. + Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(IOAddress(last_address)); + ASSERT_TRUE(from_mgr); + checkLease4(leases, last_address, from_mgr->subnet_id_, + from_mgr->hwaddr_->toText(false), true); + } + + } else { + // In the third iteration the page should be empty. + EXPECT_EQ(0, page_count->intValue()); + } + } + + // Check if all addresses were returned. + EXPECT_EQ(1, lease_addresses.count("192.0.2.1")); + EXPECT_EQ(1, lease_addresses.count("192.0.2.2")); + EXPECT_EQ(1, lease_addresses.count("192.0.3.1")); + EXPECT_EQ(1, lease_addresses.count("192.0.3.2")); +} + +// Verifies that first page of IPv4 leases can be retrieved by specifying +// zero IPv4 address. +TEST_F(LeaseCmdsTest, Lease4GetPagedZeroAddress) { + + // Initialize lease manager (false = v4, true = add a lease) + initLeaseMgr(false, true); + + // Query for a page of leases. + string cmd = + "{\n" + " \"command\": \"lease4-get-page\",\n" + " \"arguments\": {" + " \"from\": \"0.0.0.0\"," + " \"count\": 2" + " }" + "}"; + + string exp_rsp = "2 IPv4 lease(s) found."; + testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp); +} + +// Verifies that IPv6 address as a start address is rejected. +TEST_F(LeaseCmdsTest, Lease4GetPagedIPv4Address) { + + // Initialize lease manager (false = v4, true = add a lease) + initLeaseMgr(false, true); + + // Query for a page of leases. + string cmd = + "{\n" + " \"command\": \"lease4-get-page\",\n" + " \"arguments\": {" + " \"from\": \"2001:db8::1\"," + " \"count\": 2" + " }" + "}"; + + string exp_rsp = "'from' parameter value 2001:db8::1 is not an IPv4 address"; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); +} + // Checks that lease6-get-all returns all leases. TEST_F(LeaseCmdsTest, Lease6GetAll) { @@ -1791,6 +1935,229 @@ TEST_F(LeaseCmdsTest, Lease6GetBySubnetIdInvalidArguments) { testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); } +// Checks that multiple calls to lease6-get-page return all leases. +TEST_F(LeaseCmdsTest, Lease6GetPaged) { + + // Initialize lease manager (true = v6, true = add a lease) + initLeaseMgr(true, true); + + // Gather all returned addresses to verify that all were returned. + std::set<std::string> lease_addresses; + + // Keyword start indicates that we want to retrieve the first page. + std::string last_address = "start"; + + // There are 4 leases in the database, so the first two pages should + // include leases and the 3 page should be empty. + for (auto i = 0; i < 3; ++i) { + // Query for a page of leases. + string cmd = + "{\n" + " \"command\": \"lease6-get-page\",\n" + " \"arguments\": {" + " \"from\": \"" + last_address + "\"," + " \"count\": 2" + " }" + "}"; + + // For the first two pages we shuould get success. For the last + // one an empty status code. + ConstElementPtr rsp; + if (i < 2) { + string exp_rsp = "2 IPv6 lease(s) found."; + rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp); + + } else { + string exp_rsp = "0 IPv6 lease(s) found."; + rsp = testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp); + + } + + // Now check that the lease parameters were indeed returned. + ASSERT_TRUE(rsp); + + // Arguments must exist. + ConstElementPtr args = rsp->get("arguments"); + ASSERT_TRUE(args); + ASSERT_EQ(Element::map, args->getType()); + + // Each response must include total number of leases to allow the + // controlling client to track progress (e.g. percentage of leases + // already viewed). + ConstElementPtr total_count = args->get("total-count"); + ASSERT_TRUE(total_count); + ASSERT_EQ(Element::integer, total_count->getType()); + EXPECT_EQ(4, total_count->intValue()); + + // For convenience, we also return the number of returned leases, + // so as the client can check whether there was anything returned + // before parsing the leases structure. + ConstElementPtr page_count = args->get("count"); + ASSERT_TRUE(page_count); + ASSERT_EQ(Element::integer, page_count->getType()); + + // leases must exist, but may be empty. + ConstElementPtr leases = args->get("leases"); + ASSERT_TRUE(leases); + ASSERT_EQ(Element::list, leases->getType()); + + if (!leases->empty()) { + EXPECT_EQ(2, page_count->intValue()); + + // Go over each lease and verify its correctness. + for (ConstElementPtr lease : leases->listValue()) { + ASSERT_EQ(Element::map, lease->getType()); + ASSERT_TRUE(lease->contains("ip-address")); + ConstElementPtr ip_address = lease->get("ip-address"); + ASSERT_EQ(Element::string, ip_address->getType()); + last_address = ip_address->stringValue(); + + lease_addresses.insert(last_address); + + // The easiest way to retrieve the subnet id and HW address is to + // ask the Lease Manager. + Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, + IOAddress(last_address)); + ASSERT_TRUE(from_mgr); + checkLease6(leases, last_address, 0, from_mgr->subnet_id_, + from_mgr->duid_->toText(), false); + } + + } else { + // In the third iteration the page should be empty. + EXPECT_EQ(0, page_count->intValue()); + } + } + + // Check if all addresses were returned. + EXPECT_EQ(1, lease_addresses.count("2001:db8:1::1")); + EXPECT_EQ(1, lease_addresses.count("2001:db8:1::2")); + EXPECT_EQ(1, lease_addresses.count("2001:db8:2::1")); + EXPECT_EQ(1, lease_addresses.count("2001:db8:2::2")); +} + +// Verifies that first page of IPv6 leases can be retrieved by specifying +// zero IPv6 address. +TEST_F(LeaseCmdsTest, Lease6GetPagedZeroAddress) { + + // Initialize lease manager (true = v6, true = add a lease) + initLeaseMgr(true, true); + + // Query for a page of leases. + string cmd = + "{\n" + " \"command\": \"lease6-get-page\",\n" + " \"arguments\": {" + " \"from\": \"::\"," + " \"count\": 2" + " }" + "}"; + + string exp_rsp = "2 IPv6 lease(s) found."; + testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp); +} + +// Verifies that IPv4 address as a start address is rejected. +TEST_F(LeaseCmdsTest, Lease6GetPagedIPv4Address) { + + // Initialize lease manager (true = v6, true = add a lease) + initLeaseMgr(true, true); + + // Query for a page of leases. + string cmd = + "{\n" + " \"command\": \"lease6-get-page\",\n" + " \"arguments\": {" + " \"from\": \"192.0.2.3\"," + " \"count\": 2" + " }" + "}"; + + string exp_rsp = "'from' parameter value 192.0.2.3 is not an IPv6 address"; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); +} + +// Verifies that value of 'from' parameter other than 'start' or an IPv6 +// address is rejected. +TEST_F(LeaseCmdsTest, Lease6GetPagedInvalidFrom) { + + // Initialize lease manager (true = v6, true = add a lease) + initLeaseMgr(true, true); + + // Query for a page of leases. + string cmd = + "{\n" + " \"command\": \"lease6-get-page\",\n" + " \"arguments\": {" + " \"from\": \"foo\"," + " \"count\": 2" + " }" + "}"; + + string exp_rsp = "'from' parameter value is neither 'start' keyword " + "nor a valid IPv6 address"; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); +} + +// Verifies that count is mandatory. +TEST_F(LeaseCmdsTest, Lease6GetPagedNoCount) { + + // Initialize lease manager (true = v6, true = add a lease) + initLeaseMgr(true, true); + + // Query for a page of leases. + string cmd = + "{\n" + " \"command\": \"lease6-get-page\",\n" + " \"arguments\": {" + " \"from\": \"start\"" + " }" + "}"; + + string exp_rsp = "'count' parameter not specified"; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); +} + +// Verifies that the count must be a number. +TEST_F(LeaseCmdsTest, Lease6GetPagedCountNotNumber) { + + // Initialize lease manager (true = v6, true = add a lease) + initLeaseMgr(true, true); + + // Query for a page of leases. + string cmd = + "{\n" + " \"command\": \"lease6-get-page\",\n" + " \"arguments\": {" + " \"from\": \"start\"," + " \"count\": false" + " }" + "}"; + + string exp_rsp = "'count' parameter must be a number"; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); +} + +// Verifies that the count of 0 is rejected. +TEST_F(LeaseCmdsTest, Lease6GetPagedCountIsZero) { + + // Initialize lease manager (true = v6, true = add a lease) + initLeaseMgr(true, true); + + // Query for a page of leases. + string cmd = + "{\n" + " \"command\": \"lease6-get-page\",\n" + " \"arguments\": {" + " \"from\": \"start\"," + " \"count\": 0" + " }" + "}"; + + string exp_rsp = "page size of retrieved leases must not be 0"; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); +} + // Test checks if lease4-update handler refuses calls with missing parameters. TEST_F(LeaseCmdsTest, Lease4UpdateMissingParams) { // Initialize lease manager (false = v4, true = add a lease) |