From ca6f26989a7aa3bfbaf6e73028d6156d6e4a8f1f Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Wed, 10 Jan 2018 10:43:33 +0100 Subject: [PATCH] [5468] Implemented lease4-get-all command. --- src/hooks/dhcp/lease_cmds/lease_cmds.cc | 79 +++++++- src/hooks/dhcp/lease_cmds/lease_cmds.h | 25 +++ .../dhcp/lease_cmds/lease_cmds_callouts.cc | 14 +- .../lease_cmds/tests/lease_cmds_unittest.cc | 179 ++++++++++++++++-- 4 files changed, 281 insertions(+), 16 deletions(-) diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds.cc b/src/hooks/dhcp/lease_cmds/lease_cmds.cc index a5d3f25fb7..58e1af4a3d 100644 --- a/src/hooks/dhcp/lease_cmds/lease_cmds.cc +++ b/src/hooks/dhcp/lease_cmds/lease_cmds.cc @@ -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 #include +#include 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& 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)); diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds.h b/src/hooks/dhcp/lease_cmds/lease_cmds.h index 03e2cdcaf1..106ff92642 100644 --- a/src/hooks/dhcp/lease_cmds/lease_cmds.h +++ b/src/hooks/dhcp/lease_cmds/lease_cmds.h @@ -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 diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds_callouts.cc b/src/hooks/dhcp/lease_cmds/lease_cmds_callouts.cc index 51afc10fe3..809c46d83e 100644 --- a/src/hooks/dhcp/lease_cmds/lease_cmds_callouts.cc +++ b/src/hooks/dhcp/lease_cmds/lease_cmds_callouts.cc @@ -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); 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 ff0d3b9a44..5deecbd12d 100644 --- a/src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc +++ b/src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc @@ -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(6, 0x08), HTYPE_ETHER)); - lease->client_id_ = ClientIdPtr(new ClientId(vector(8, 0x42))); + lease->hwaddr_.reset(new HWAddr(vector(6, hw_address_pattern), HTYPE_ETHER)); + lease->client_id_ = ClientIdPtr(new ClientId(vector(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 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. -- 2.47.2