summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcin Siodelski <marcin@isc.org>2018-06-26 21:36:43 +0200
committerMarcin Siodelski <marcin@isc.org>2018-06-26 21:36:43 +0200
commit2e632454da934fd674b353dccb664804bde9d88f (patch)
tree6f5db0bedbdd67641b86b2d9e9e0cfa8ef3c571b
parent[5651] Guard against invalid address family. (diff)
downloadkea-2e632454da934fd674b353dccb664804bde9d88f.tar.xz
kea-2e632454da934fd674b353dccb664804bde9d88f.zip
[5651] Implemented leaseX-get-page commands.
-rw-r--r--src/hooks/dhcp/lease_cmds/lease_cmds.cc160
-rw-r--r--src/hooks/dhcp/lease_cmds/lease_cmds.h17
-rw-r--r--src/hooks/dhcp/lease_cmds/lease_cmds_callouts.cc26
-rw-r--r--src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc377
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)