2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-22 09:57:41 +00:00

[#3811] Return sn name in subnet lists commands

/doc/sphinx/arm/hooks-subnet-cmds.rst
    Updated doc to include shared-network-name in returns

/src/hooks/dhcp/subnet_cmds/subnet_cmds.cc
    subnetToElement() - now optionally inserts the shared network name

/src/hooks/dhcp/subnet_cmds/tests/subnet4_cmds_unittest.cc
/src/hooks/dhcp/subnet_cmds/tests/subnet6_cmds_unittest.cc
/src/hooks/dhcp/subnet_cmds/tests/subnet6_cmds_unittest.cc
    updated tests

added change log entry
This commit is contained in:
Thomas Markwalder 2025-04-02 09:37:01 -04:00
parent b66fe236f8
commit 11504bff67
6 changed files with 110 additions and 62 deletions

View File

@ -0,0 +1,6 @@
[func] tmark
The subnet commands hook library was modified such that
the subnet's shared network name is now included for each
subnet returned by the subnet4-list and subnet6-list
commands.
(Gitlab #3811)

View File

@ -94,11 +94,13 @@ The list of subnets is returned in the following format:
"subnets": [
{
"id": 10,
"shared-network-name": "net1",
"subnet": "10.0.0.0/8"
},
{
"id": 100,
"subnet": "192.0.2.0/24"
"shared-network-name": null,
"subnet": "192.0.2.0/24",
}
]
}
@ -137,10 +139,12 @@ The list of subnets is returned in the following format:
"subnets": [
{
"id": 11,
"shared-network-name": null,
"subnet": "2001:db8:1::/64"
},
{
"id": 233,
"shared-network-name": "some-net",
"subnet": "3000::/16"
}
]

View File

@ -347,7 +347,7 @@ public:
// Iterate over all subnets and retrieve the information we're interested in.
for (auto const& s : *subnets) {
// Information for the individual subnets is held in the map.
subnet_list->add(subnetToElement(*s));
subnet_list->add(subnetToElement(*s, true));
}
// Generate the status message including the number of subnets found.
@ -927,7 +927,7 @@ public:
ElementPtr details = Element::createMap();
ElementPtr lst = Element::createList();
lst->add(subnetToElement(*subnet));
lst->add(subnetToElement(*subnet, false));
details->set("subnets", lst);
// Create the response.
@ -945,11 +945,22 @@ public:
///
/// @brief subnet details of that subnet will be returned
/// @return id and subnet in Element format
ElementPtr subnetToElement(const Subnet& subnet) const {
ElementPtr subnetToElement(const Subnet& subnet, bool include_shared_network) const {
ElementPtr subnet_element = Element::createMap();
subnet_element->set("id",
Element::create(static_cast<long int>(subnet.getID())));
subnet_element->set("subnet", Element::create(subnet.toText()));
if (include_shared_network) {
std::string sn_name = subnet.getSharedNetworkName();
if (!sn_name.empty()) {
subnet_element->set("shared-network-name", data::Element::create(sn_name));
} else {
// Shared network name is null.
subnet_element->set("shared-network-name", data::Element::create());
}
}
return (subnet_element);
}

View File

@ -1742,9 +1742,9 @@ public:
// of sending the 'subnet4-list' command.
TEST_F(Subnet4CmdsTest, subnet4List) {
// Add several IPv4 subnets to the server configuration.
addSubnet("10.0.0.0/8", SubnetID(5));
addSubnet("10.0.0.0/8", SubnetID(5), "net1");
addSubnet("192.168.50.0/24", SubnetID(10));
addSubnet("192.0.2.0/29", SubnetID(123));
addSubnet("192.0.2.0/29", SubnetID(123), "net2");
// Add one IPv6 subnet to the configuration. We want to make sure that
// this subnet is not returned within the response to the command.
addSubnet("2001:db8:1::/64", SubnetID(17));
@ -1758,15 +1758,31 @@ TEST_F(Subnet4CmdsTest, subnet4List) {
CONTROL_RESULT_SUCCESS,
"3 IPv4 subnets found");
// Verify that the response has appropriate structure.
ASSERT_NO_FATAL_FAILURE(checkSubnetListStructure(response, 3));
// Verify that the response has appropriate structure and returned
// the expected arguments.
std::string exp_args=R"(
{
"subnets": [
{
"id": 5,
"shared-network-name": "net1",
"subnet": "10.0.0.0/8"
},
{
"id": 10,
"shared-network-name": null,
"subnet": "192.168.50.0/24"
},
{
"id": 123,
"shared-network-name": "net2",
"subnet": "192.0.2.0/29"
}
]
}
)";
// Our 3 IPv4 subnets should be returned in the response.
ASSERT_TRUE(hasSubnet(response, "10.0.0.0/8", SubnetID(5)));
ASSERT_TRUE(hasSubnet(response, "192.168.50.0/24", SubnetID(10)));
ASSERT_TRUE(hasSubnet(response, "192.0.2.0/29", SubnetID(123)));
// The IPv6 subnet should not be included.
ASSERT_FALSE(hasSubnet(response, "2001:db8:1::/64", SubnetID(17)));
ASSERT_NO_FATAL_FAILURE(checkResponseArgs(response, exp_args));
}
// This test verifies that 'subnet4-list' returns empty list of subnets when
@ -1780,7 +1796,13 @@ TEST_F(Subnet4CmdsTest, noSubnet4List) {
// Verify that the response has appropriate structure and no subnets
// are included.
ASSERT_NO_FATAL_FAILURE(checkSubnetListStructure(response, 0));
std::string exp_args=R"(
{
"subnets": [ ]
}
)";
ASSERT_NO_FATAL_FAILURE(checkResponseArgs(response, exp_args));
}
// This test verifies that IPv4 subnet can be retrieved by subnet id

View File

@ -2433,9 +2433,9 @@ public:
// of sending the 'subnet6-list' command.
TEST_F(Subnet6CmdsTest, subnet6List) {
// Add several IPv6 subnets to the server configuration.
addSubnet("3000::/16", SubnetID(9));
addSubnet("3000::/16", SubnetID(9), "net1");
addSubnet("3001:23::/92", SubnetID(11));
addSubnet("2001:db8:1::/64", SubnetID(1023));
addSubnet("2001:db8:1::/64", SubnetID(1023), "net2");
// Add one IPv4 subnet to the configuration. We want to make sure that
// this subnet is not returned within the response to the command.
addSubnet("10.2.30.0/24", SubnetID(33));
@ -2449,15 +2449,30 @@ TEST_F(Subnet6CmdsTest, subnet6List) {
CONTROL_RESULT_SUCCESS,
"3 IPv6 subnets found");
// Verify that the response has appropriate structure.
ASSERT_NO_FATAL_FAILURE(checkSubnetListStructure(response, 3));
// Verify that the response has appropriate structure and returned
// the expected arguments.
std::string exp_args=R"(
{
"subnets": [
{
"id": 9,
"shared-network-name": "net1",
"subnet": "3000::/16"
},
{
"id": 11,
"shared-network-name": null,
"subnet": "3001:23::/92"
},
{
"id": 1023,
"shared-network-name": "net2",
"subnet": "2001:db8:1::/64"
}]
}
)";
// Our 3 IPv6 subnets should be returned in the response.
ASSERT_TRUE(hasSubnet(response, "3000::/16", SubnetID(9)));
ASSERT_TRUE(hasSubnet(response, "3001:23::/92", SubnetID(11)));
ASSERT_TRUE(hasSubnet(response, "2001:db8:1::/64", SubnetID(1023)));
// The IPv4 subnet should not be included.
ASSERT_FALSE(hasSubnet(response, "10.2.30.0/24", SubnetID(33)));
ASSERT_NO_FATAL_FAILURE(checkResponseArgs(response, exp_args));
}
// This test verifies that 'subnet6-list' returns empty list of subnets when
@ -2468,10 +2483,15 @@ TEST_F(Subnet6CmdsTest, noSubnet6List) {
ConstElementPtr response = testCommand("{ \"command\": \"subnet6-list\" }",
CONTROL_RESULT_EMPTY,
"0 IPv6 subnets found");
// Verify that the response has appropriate structure and no subnets
// are included.
ASSERT_NO_FATAL_FAILURE(checkSubnetListStructure(response, 0));
std::string exp_args=R"(
{
"subnets": [ ]
}
)";
ASSERT_NO_FATAL_FAILURE(checkResponseArgs(response, exp_args));
}
// This test verifies that IPv6 subnet can be retrieved by subnet id

View File

@ -16,6 +16,8 @@
#include <dhcpsrv/subnet_id.h>
#include <process/daemon.h>
#include <stats/stats_mgr.h>
#include <testutils/gtest_utils.h>
#include <testutils/test_to_element.h>
#include <boost/lexical_cast.hpp>
#include <gtest/gtest.h>
#include <sstream>
@ -172,7 +174,9 @@ public:
/// @param subnet_str Subnet in a textual form, e.g. 10.0.0.0/8 or
/// 2001:db8::/64.
/// @param subnet_id Subnet id.
void addSubnet(const std::string& subnet_str, const isc::dhcp::SubnetID& subnet_id) {
/// @param shared_network_name name of shared-network, defaults to empty string.
void addSubnet(const std::string& subnet_str, const isc::dhcp::SubnetID& subnet_id,
const std::string& shared_network_name = "") {
try {
// Split prefix and prefix length.
auto split_pos = subnet_str.find('/');
@ -193,12 +197,20 @@ public:
if (prefix.isV4()) {
isc::dhcp::CfgSubnets4Ptr cfg = isc::dhcp::CfgMgr::instance().getStagingCfg()->getCfgSubnets4();
isc::dhcp::Subnet4Ptr subnet(new isc::dhcp::Subnet4(prefix, prefix_length, 30, 40, 60, subnet_id));
if (!shared_network_name.empty()) {
subnet->setSharedNetworkName(shared_network_name);
}
cfg->add(subnet);
addReservations(subnet, isc::dhcp::CfgMgr::instance().getStagingCfg()->getCfgHosts());
} else {
isc::dhcp::CfgSubnets6Ptr cfg = isc::dhcp::CfgMgr::instance().getStagingCfg()->getCfgSubnets6();
isc::dhcp::Subnet6Ptr subnet(new isc::dhcp::Subnet6(prefix, prefix_length, 30, 40, 50, 60, subnet_id));
if (!shared_network_name.empty()) {
subnet->setSharedNetworkName(shared_network_name);
}
cfg->add(subnet);
addReservations(subnet, isc::dhcp::CfgMgr::instance().getStagingCfg()->getCfgHosts());
}
@ -479,49 +491,22 @@ public:
}
}
/// @brief Verifies that the structure of the list of subnets returned
/// contains allowed values.
/// @brief Verifies that the content of the "args" element in the response.
///
/// @param answer Server's response to a command.
/// @param exp_list_size Expected number of subnets returned.
void checkSubnetListStructure(const isc::data::ConstElementPtr& answer,
const size_t exp_list_size) {
/// @param exp_args JSON map of the expected arguments contents.
void checkResponseArgs(const isc::data::ConstElementPtr& answer,
const std::string exp_args) {
// Retrieve "arguments" and make sure it is a map.
int rcode = 0;
isc::data::ConstElementPtr args = isc::config::parseAnswer(rcode, answer);
ASSERT_TRUE(args);
ASSERT_EQ(isc::data::Element::map, args->getType());
// Retrieve "subnets" list from "arguments".
isc::data::ConstElementPtr subnet_ids = args->get("subnets");
ASSERT_TRUE(subnet_ids);
EXPECT_EQ(isc::data::Element::list, subnet_ids->getType());
isc::data::ConstElementPtr exp_args_map;
ASSERT_NO_THROW_LOG(exp_args_map = isc::data::Element::fromJSON(exp_args));
// Make sure that the list contains expected number of subnets.
EXPECT_EQ(exp_list_size, subnet_ids->size());
// Iterate over the list of subnets in the list and make sure that
// it contains expected parameters. It does not verify the values though.
for (unsigned index = 0; index != subnet_ids->size(); ++index) {
// Get the element in the list.
isc::data::ConstElementPtr subnet_element = subnet_ids->get(index);
ASSERT_TRUE(subnet_element);
// There should be exactly two parameters: "id" and "subnet".
ASSERT_EQ(2, subnet_element->size());
ASSERT_TRUE(subnet_element->contains("id"));
ASSERT_TRUE(subnet_element->contains("subnet"));
// Subnet identifier is a number.
isc::data::ConstElementPtr subnet_id = subnet_element->get("id");
ASSERT_TRUE(subnet_id);
EXPECT_EQ(isc::data::Element::integer, subnet_id->getType());
// Subnet prefix is a string.
isc::data::ConstElementPtr subnet_value = subnet_element->get("subnet");
ASSERT_TRUE(subnet_value);
EXPECT_EQ(isc::data::Element::string, subnet_value->getType());
}
isc::test::expectEqWithDiff(args, exp_args_map);
}
/// @brief Checks that the response includes a given subnet.