]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[5468] Implemented lease4-get-all command.
authorMarcin Siodelski <marcin@isc.org>
Wed, 10 Jan 2018 09:43:33 +0000 (10:43 +0100)
committerMarcin Siodelski <marcin@isc.org>
Wed, 10 Jan 2018 09:43:33 +0000 (10:43 +0100)
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 a5d3f25fb7416dbb6894a4a1451f9a63d919f15f..58e1af4a3dc7715eda5541d1b1fd4e8e65bad451 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2017-2018 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -25,6 +25,7 @@
 
 #include <boost/bind.hpp>
 #include <string>
+#include <sstream>
 
 using namespace isc::dhcp;
 using namespace isc::data;
@@ -129,6 +130,18 @@ public:
     int
     leaseGetHandler(CalloutHandle& handle);
 
+    /// @brief lease4-get-all command handler
+    ///
+    /// This command attempts to retrieve all IPv4 leases or all IPv4 leases
+    /// belonging to the particular subnets. If no subnet identifiers are
+    /// provided, it returns all IPv4 leases from the database.
+    ///
+    /// @param handle Callout context - which is expected to contain the
+    /// get command JSON text in the "command" argument
+    /// return 0 upon success, non-zero otherwise.
+    int
+    lease4GetAllHandler(CalloutHandle& handle);
+
     /// @brief lease4-del command handler
     ///
     /// Provides the implementation for @ref isc::lease_cmds::LeaseCmds::lease4DelHandler
@@ -455,6 +468,65 @@ LeaseCmdsImpl::leaseGetHandler(CalloutHandle& handle) {
     return (0);
 }
 
+int
+LeaseCmdsImpl::lease4GetAllHandler(CalloutHandle& handle) {
+    try {
+        extractCommand(handle);
+
+        ElementPtr leases_json = Element::createList();
+
+        // The argument may contain a list of subnets for which leases should
+        // be returned.
+        if (cmd_args_) {
+            ConstElementPtr subnets = cmd_args_->get("subnets");
+            if (subnets) {
+                if (subnets->getType() != Element::list) {
+                    isc_throw(BadValue, "'subnets' parameter must be a list");
+                }
+
+                const std::vector<ElementPtr>& subnet_ids = subnets->listValue();
+                for (auto subnet_id = subnet_ids.begin(); subnet_id != subnet_ids.end();
+                     ++subnet_id) {
+                    if ((*subnet_id)->getType() != Element::integer) {
+                        isc_throw(BadValue, "listed subnet identifiers must be numbers");
+                    }
+
+                    Lease4Collection leases =
+                        LeaseMgrFactory::instance().getLeases4((*subnet_id)->intValue());
+                    for (auto lease = leases.begin(); lease != leases.end(); ++lease) {
+                        ElementPtr lease_json = (*lease)->toElement();
+                        leases_json->add(lease_json);
+                    }
+                }
+
+            } else {
+                isc_throw(BadValue, "'subnets' parameter not specified");
+            }
+
+        } else {
+            // There is no 'subnets' argument so let's return all leases.
+            Lease4Collection leases = LeaseMgrFactory::instance().getLeases4();
+            for (auto lease = leases.begin(); lease != leases.end(); ++lease) {
+                ElementPtr lease_json = (*lease)->toElement();
+                leases_json->add(lease_json);
+            }
+        }
+
+        std::ostringstream s;
+        s << leases_json->size() << " IPv4 lease(s) found.";
+        ConstElementPtr response = createAnswer(CONTROL_RESULT_SUCCESS,
+                                                s.str(), leases_json);
+        setResponse(handle, response);
+
+
+    } catch (const std::exception& ex) {
+        setErrorResponse(handle, ex.what());
+        return (CONTROL_RESULT_ERROR);
+    }
+
+    return (0);
+}
+
 int
 LeaseCmdsImpl::lease4DelHandler(CalloutHandle& handle) {
     Parameters p;
@@ -707,6 +779,11 @@ LeaseCmds::leaseGetHandler(CalloutHandle& handle) {
     return(impl_->leaseGetHandler(handle));
 }
 
+int
+LeaseCmds::lease4GetAllHandler(hooks::CalloutHandle& handle) {
+    return (impl_->lease4GetAllHandler(handle));
+}
+
 int
 LeaseCmds::lease4DelHandler(CalloutHandle& handle) {
     return(impl_->lease4DelHandler(handle));
index 03e2cdcaf1dfe6e1405f948d094e2c83c5096d36..106ff92642e0f59226bc3a3d51762e2ce5075e10 100644 (file)
@@ -131,6 +131,31 @@ public:
     int
     leaseGetHandler(hooks::CalloutHandle& handle);
 
+    /// @brief lease4-get-all command handler
+    ///
+    /// This command attempts to retrieve all IPv4 leases or all IPv4 leases
+    /// belonging to the particular subnets. If no subnet identifiers are
+    /// provided, it returns all IPv4 leases from the database.
+    ///
+    /// Example command for query by (subnet-ids):
+    /// {
+    ///     "command": "lease4-get-all",
+    ///     "arguments": {
+    ///         "subnets": [ 1, 2, 3, 4 ]
+    ///     }
+    /// }
+    ///
+    /// Example command for retrieving all leases:
+    /// {
+    ///     "command": "lease4-get-all",
+    /// }
+    ///
+    /// @param handle Callout context - which is expected to contain the
+    /// get command JSON text in the "command" argument
+    /// @return result of the operation.
+    int
+    lease4GetAllHandler(hooks::CalloutHandle& handle);
+
     /// @brief lease4-del command handler
     ///
     /// This command attempts to delete an IPv4 lease that match selected
index 51afc10fe386cbfc3a2e7bc4bf67c799c85343a1..809c46d83ea8b7ed95d77bf6e0e4c29c02efe2c5 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2017-2018 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the End User License
 // Agreement. See COPYING file in the premium/ directory.
@@ -53,6 +53,17 @@ int lease4_get(CalloutHandle& handle) {
     return(lease_cmds.leaseGetHandler(handle));
 }
 
+/// @brief This is a command callout for 'lease4-get-all' command.
+///
+/// @param handle Callout handle used to retrieve a command and
+/// provide a response.
+/// @return 0 if this callout has been invoked successfully,
+/// 1 otherwise.
+int lease4_get_all(CalloutHandle& handle) {
+    LeaseCmds lease_cmds;
+    return (lease_cmds.lease4GetAllHandler(handle));
+}
+
 /// @brief This is a command callout for 'lease6-get' command.
 ///
 /// @param handle Callout handle used to retrieve a command and
@@ -138,6 +149,7 @@ 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-del", lease4_del);
     handle.registerCommandCallout("lease6-del", lease6_del);
index ff0d3b9a4468b5b6aa04e159fa3e1dfb37702e74..5deecbd12d896118d173c9ea480b570963023eb8 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2017-2018 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -260,9 +260,11 @@ public:
             subnets->add(subnet6);
             cfg_mgr.commit();
         } else {
-            Subnet4Ptr subnet4(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3, 44));
+            Subnet4Ptr subnet44(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3, 44));
+            Subnet4Ptr subnet88(new Subnet4(IOAddress("192.0.3.0"), 24, 1, 2, 3, 88));
             CfgSubnets4Ptr subnets = cfg_mgr.getStagingCfg()->getCfgSubnets4();
-            subnets->add(subnet4);
+            subnets->add(subnet44);
+            subnets->add(subnet88);
             cfg_mgr.commit();
         }
 
@@ -270,34 +272,43 @@ public:
             if (v6) {
                 lmptr_->addLease(createLease6());
             } else {
-                lmptr_->addLease(createLease4());
+                lmptr_->addLease(createLease4("192.0.2.1", 44, 0x08, 0x42));
+                lmptr_->addLease(createLease4("192.0.2.2", 44, 0x09, 0x56));
+                lmptr_->addLease(createLease4("192.0.3.1", 88, 0x08, 0x42));
+                lmptr_->addLease(createLease4("192.0.3.2", 88, 0x09, 0x56));
             }
         }
     }
 
     /// @brief Creates an IPv4 lease
     ///
-    /// Lease parameters: ip-address = 192.0.2.1, hwaddr = 08:08:08:08:08:08,
-    /// client-id = 42:42:42:42:42:42:42:42, valid lifetime = 3600,
-    /// cltt = 12345678, subnet-id = 44, fqdn-fwd = false, fqdn-rev = true
-    /// hostname = myhost.example.com
+    /// Lease parameters: valid lifetime = 3600, cltt = 12345678, fqdn-fwd = false,
+    /// fqdn-rev = true, hostname = myhost.example.com
     ///
+    /// @param ip_address IP address for the lease.
+    /// @param subnet_id subnet identifier
+    /// @param hw_address_pattern value to be used for generating HW address by repating
+    /// it 6 times.
+    /// @param client_id_pattern value to be used for generating client identifier by
+    /// repeating it 8 times.
     /// @return Returns the lease created
-    Lease4Ptr createLease4() {
+    Lease4Ptr createLease4(const std::string& ip_address, const SubnetID& subnet_id,
+                           const uint8_t hw_address_pattern,
+                           const uint8_t client_id_pattern) {
         Lease4Ptr lease(new Lease4());
 
-        lease->addr_ = IOAddress("192.0.2.1");
+        lease->addr_ = IOAddress(ip_address);
 
         // Initialize unused fields.
         lease->t1_ = 0;                             // Not saved
         lease->t2_ = 0;                             // Not saved
 
         // Set other parameters.  For historical reasons, address 0 is not used.
-        lease->hwaddr_.reset(new HWAddr(vector<uint8_t>(6, 0x08), HTYPE_ETHER));
-        lease->client_id_ = ClientIdPtr(new ClientId(vector<uint8_t>(8, 0x42)));
+        lease->hwaddr_.reset(new HWAddr(vector<uint8_t>(6, hw_address_pattern), HTYPE_ETHER));
+        lease->client_id_ = ClientIdPtr(new ClientId(vector<uint8_t>(8, client_id_pattern)));
         lease->valid_lft_ = 3600;
         lease->cltt_ = 12345678;
-        lease->subnet_id_ = 44;
+        lease->subnet_id_ = subnet_id;
         lease->fqdn_fwd_ = false;
         lease->fqdn_rev_ = true;
         lease->hostname_ = "myhost.example.com.";
@@ -343,6 +354,20 @@ public:
                      uint32_t subnet_id, std::string hwaddr,
                      bool client_id_required) {
         ASSERT_TRUE(l);
+
+        // If the element is a list we need to retrieve the lease that
+        // we're interested in.
+        if (l->getType() == Element::list) {
+            std::vector<ElementPtr> e = l->listValue();
+            for (auto it = e.begin(); it != e.end(); ++it) {
+                ConstElementPtr ip_address = (*it)->get("ip-address");
+                if (ip_address && ip_address->stringValue() == ip) {
+                    l = (*it);
+                    break;
+                }
+            }
+        }
+
         ASSERT_TRUE(l->get("ip-address"));
         EXPECT_EQ(ip, l->get("ip-address")->stringValue());
 
@@ -1099,6 +1124,132 @@ TEST_F(LeaseCmdsTest, Lease4GetByHWAddr) {
     checkLease4(lease, "192.0.2.1", 44, "08:08:08:08:08:08", false);
 }
 
+// Checks that lease4-get-all returns all leases.
+TEST_F(LeaseCmdsTest, Lease4GetAll) {
+
+    // Initialize lease manager (false = v4, true = add a lease)
+    initLeaseMgr(false, true);
+
+    // Query for all leases.
+    string cmd =
+        "{\n"
+        "    \"command\": \"lease4-get-all\"\n"
+        "}";
+    string exp_rsp = "4 IPv4 lease(s) found.";
+    ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+    // Now check that the lease parameters were indeed returned.
+    ASSERT_TRUE(rsp);
+    ConstElementPtr leases = rsp->get("arguments");
+    ASSERT_TRUE(leases);
+    ASSERT_EQ(Element::list, leases->getType());
+
+    // Let's check if the response contains desired leases.
+    checkLease4(leases, "192.0.2.1", 44, "08:08:08:08:08:08", true);
+    checkLease4(leases, "192.0.2.2", 44, "09:09:09:09:09:09", true);
+    checkLease4(leases, "192.0.3.1", 88, "08:08:08:08:08:08", true);
+    checkLease4(leases, "192.0.3.2", 88, "09:09:09:09:09:09", true);
+}
+
+// Checks that lease4-get-all returns all leases for a subnet.
+TEST_F(LeaseCmdsTest, Lease4GetAllBySubnetId) {
+
+    // Initialize lease manager (false = v4, true = add a lease)
+    initLeaseMgr(false, true);
+
+    // Query for leases from subnet 44. Subnet 127 will be ignored because
+    // it doesn't contain any leases.
+    string cmd =
+        "{\n"
+        "    \"command\": \"lease4-get-all\",\n"
+        "    \"arguments\": {\n"
+        "        \"subnets\": [ 44, 127 ]"
+        "    }\n"
+        "}";
+    string exp_rsp = "2 IPv4 lease(s) found.";
+    ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+    // Now check that the lease parameters were indeed returned.
+    ASSERT_TRUE(rsp);
+    ConstElementPtr leases = rsp->get("arguments");
+    ASSERT_TRUE(leases);
+    ASSERT_EQ(Element::list, leases->getType());
+
+    // Let's check if the response contains desired leases.
+    checkLease4(leases, "192.0.2.1", 44, "08:08:08:08:08:08", true);
+    checkLease4(leases, "192.0.2.2", 44, "09:09:09:09:09:09", true);
+}
+
+// Checks that lease4-get-all returns leases from multiple subnets.
+TEST_F(LeaseCmdsTest, Lease4GetAllByMultipleSubnetIds) {
+
+    // Initialize lease manager (false = v4, true = add a lease)
+    initLeaseMgr(false, true);
+
+    // Query for leases from subnet 44 and 88.
+    string cmd =
+        "{\n"
+        "    \"command\": \"lease4-get-all\",\n"
+        "    \"arguments\": {\n"
+        "        \"subnets\": [ 44, 88 ]"
+        "    }\n"
+        "}";
+    string exp_rsp = "4 IPv4 lease(s) found.";
+    ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+    // Now check that the lease parameters were indeed returned.
+    ASSERT_TRUE(rsp);
+    ConstElementPtr leases = rsp->get("arguments");
+    ASSERT_TRUE(leases);
+    ASSERT_EQ(Element::list, leases->getType());
+
+    // Let's check if the response contains desired leases.
+    checkLease4(leases, "192.0.2.1", 44, "08:08:08:08:08:08", true);
+    checkLease4(leases, "192.0.2.2", 44, "09:09:09:09:09:09", true);
+    checkLease4(leases, "192.0.3.1", 88, "08:08:08:08:08:08", true);
+    checkLease4(leases, "192.0.3.2", 88, "09:09:09:09:09:09", true);
+}
+
+// Checks that lease4-get-all checks its input arguments.
+TEST_F(LeaseCmdsTest, Lease4GetBySubnetIdInvalidArguments) {
+
+    // Initialize lease manager (false = v4, true = add a lease)
+    initLeaseMgr(false, true);
+
+    // Subnets not specified in arguments.
+    string cmd =
+        "{\n"
+        "    \"command\": \"lease4-get-all\",\n"
+        "    \"arguments\": {"
+        "        \"foo\": 1\n"
+        "    }\n"
+        "}";
+    string exp_rsp = "'subnets' parameter not specified";
+    testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+    // Subnets are not a list.
+    cmd =
+        "{\n"
+        "    \"command\": \"lease4-get-all\",\n"
+        "    \"arguments\": {"
+        "        \"subnets\": 1\n"
+        "    }\n"
+        "}";
+    exp_rsp = "'subnets' parameter must be a list";
+    testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+    // Subnets list must contain numbers.
+    cmd =
+        "{\n"
+        "    \"command\": \"lease4-get-all\",\n"
+        "    \"arguments\": {"
+        "        \"subnets\": [ \"x\", \"y\" ]\n"
+        "    }\n"
+        "}";
+    exp_rsp = "listed subnet identifiers must be numbers";
+    testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
 // Checks that lease6-get(addr) can handle a situation when
 // the query is correctly formed, but the lease is not there.
 TEST_F(LeaseCmdsTest, Lease6GetByAddr6NotFound) {
@@ -2098,7 +2249,7 @@ TEST_F(LeaseCmdsTest, Lease4Wipe) {
         "        \"subnet-id\": 44"
         "    }\n"
         "}";
-    string exp_rsp = "Deleted 1 IPv4 lease(s).";
+    string exp_rsp = "Deleted 2 IPv4 lease(s).";
     testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
 
     // Make sure the lease is really gone.