]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[5651] Implemented leaseX-get-page commands.
authorMarcin Siodelski <marcin@isc.org>
Tue, 26 Jun 2018 19:36:43 +0000 (21:36 +0200)
committerMarcin Siodelski <marcin@isc.org>
Tue, 26 Jun 2018 19:36:43 +0000 (21:36 +0200)
src/hooks/dhcp/lease_cmds/lease_cmds.cc
src/hooks/dhcp/lease_cmds/lease_cmds.h
src/hooks/dhcp/lease_cmds/lease_cmds_callouts.cc
src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc

index 19f0c4794a89f9cfca754f0a9b4b66d33da38ee7..955e75d6da739b77d0365347ca9e8c81974f8cfb 100644 (file)
@@ -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
@@ -554,6 +572,143 @@ LeaseCmdsImpl::leaseGetAllHandler(CalloutHandle& handle) {
     return (0);
 }
 
+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;
@@ -867,6 +1022,11 @@ LeaseCmds::leaseGetAllHandler(hooks::CalloutHandle& handle) {
     return (impl_->leaseGetAllHandler(handle));
 }
 
+int
+LeaseCmds::leaseGetPageHandler(hooks::CalloutHandle& handle) {
+    return (impl_->leaseGetPageHandler(handle));
+}
+
 int
 LeaseCmds::lease4DelHandler(CalloutHandle& handle) {
     return(impl_->lease4DelHandler(handle));
index 7b8fd123a3c76098bd7b164c0cb9c2c05807cdb9..ff842480f108fd753d64974835944fd7454fbdfb 100644 (file)
@@ -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
index 97912ce71e3977115a96c0e5d71d44c9f344bd25..12115941e7054ec4e1526cb1bbd9315a9b9b8b2a 100644 (file)
@@ -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);
index 04b3252695ab06725ae4359a64a006f0e053b6c0..315a86f1e5e853864957d7129877879c0a31c8c3 100644 (file)
@@ -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)