Commit b0cca798 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[5305] Parsers for shared networks added.

parent bfaf5c81
......@@ -105,6 +105,13 @@ protected:
/// @brief Represents configuration of IPv4 shared networks.
class CfgSharedNetworks4 : public CfgSharedNetworks<SharedNetwork4Ptr> {
public:
/// @brief Returns pointer to all configured shared networks.
const SharedNetwork4Collection* getAll() const {
return (&networks_);
}
};
/// @brief Pointer to the configuration of IPv4 shared networks.
......@@ -112,6 +119,12 @@ typedef boost::shared_ptr<CfgSharedNetworks4> CfgSharedNetworks4Ptr;
/// @brief Represents configuration of IPv6 shared networks.
class CfgSharedNetworks6 : public CfgSharedNetworks<SharedNetwork6Ptr> {
public:
/// @brief Returns pointer to all configured shared networks.
const SharedNetwork6Collection* getAll() const {
return (&networks_);
}
};
/// @brief Pointer to the configuration of IPv6 shared networks.
......
......@@ -809,6 +809,28 @@ Subnets4ListConfigParser::parse(SrvConfigPtr cfg, ConstElementPtr subnets_list)
return (cnt);
}
size_t
Subnets4ListConfigParser::parse(Subnet4Collection& subnets,
data::ConstElementPtr subnets_list) {
size_t cnt = 0;
BOOST_FOREACH(ConstElementPtr subnet_json, subnets_list->listValue()) {
Subnet4ConfigParser parser;
Subnet4Ptr subnet = parser.parse(subnet_json);
if (subnet) {
try {
subnets.push_back(subnet);
++cnt;
} catch (const std::exception& ex) {
isc_throw(DhcpConfigError, ex.what() << " ("
<< subnet_json->getPosition() << ")");
}
}
}
return (cnt);
}
//**************************** Pool6Parser *********************************
PoolPtr
......@@ -1062,6 +1084,25 @@ Subnets6ListConfigParser::parse(SrvConfigPtr cfg, ConstElementPtr subnets_list)
return (cnt);
}
size_t
Subnets6ListConfigParser::parse(Subnet6Collection& subnets,
ConstElementPtr subnets_list) {
size_t cnt = 0;
BOOST_FOREACH(ConstElementPtr subnet_json, subnets_list->listValue()) {
Subnet6ConfigParser parser;
Subnet6Ptr subnet = parser.parse(subnet_json);
try {
subnets.push_back(subnet);
++cnt;
} catch (const std::exception& ex) {
isc_throw(DhcpConfigError, ex.what() << " ("
<< subnet_json->getPosition() << ")");
}
}
return (cnt);
}
//**************************** D2ClientConfigParser **********************
......
......@@ -672,6 +672,14 @@ public:
/// @param subnets_list pointer to a list of IPv4 subnets
/// @return number of subnets created
size_t parse(SrvConfigPtr cfg, data::ConstElementPtr subnets_list);
/// @brief Parses contents of the subnet4 list.
///
/// @param [out] subnets Container where parsed subnets will be stored.
/// @param subnets_list pointer to a list of IPv4 subnets
/// @return Number of subnets created.
size_t parse(Subnet4Collection& subnets,
data::ConstElementPtr subnets_list);
};
/// @brief Parser for IPv6 pool definitions.
......@@ -849,6 +857,15 @@ public:
/// @param subnets_list pointer to a list of IPv6 subnets
/// @throw DhcpConfigError if CfgMgr rejects the subnet (e.g. subnet-id is a duplicate)
size_t parse(SrvConfigPtr cfg, data::ConstElementPtr subnets_list);
/// @brief Parses contents of the subnet6 list.
///
/// @param [out] subnets Container where parsed subnets will be stored.
/// @param subnets_list pointer to a list of IPv6 subnets
/// @return Number of subnets created.
size_t parse(Subnet6Collection& subnets,
data::ConstElementPtr subnets_list);
};
/// @brief Parser for D2ClientConfig
......
......@@ -9,6 +9,7 @@
#include <cc/data.h>
#include <cc/simple_parser.h>
#include <dhcp/option_definition.h>
#include <dhcpsrv/cfg_option.h>
#include <util/optional_value.h>
#include <cstdint>
......
......@@ -8,20 +8,121 @@
#define SHARED_NETWORK_PARSER_H
#include <cc/data.h>
#include <dhcpsrv/cfg_option.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
#include <dhcpsrv/parsers/option_data_parser.h>
#include <dhcpsrv/parsers/shared_network_parser.h>
#include <dhcpsrv/shared_network.h>
#include <boost/pointer_cast.hpp>
#include <string>
using namespace isc::data;
namespace isc {
namespace dhcp {
SharedNetworkParser::~SharedNetworkParser() {
SharedNetwork4Ptr
SharedNetwork4Parser::parse(const data::ConstElementPtr& shared_network_data) {
SharedNetwork4Ptr shared_network;
std::string name;
try {
// Shared network is a map.
const auto& element = shared_network_data->mapValue();
// Make sure that the network name has been specified. The name is required
// to create a SharedNetwork4 object.
const auto& name_it = element.find("name");
if (name_it == element.cend()) {
isc_throw(DhcpConfigError, "parameter \"name\" must be specified for"
" a shared network");
}
shared_network.reset(new SharedNetwork4(name_it->second->stringValue()));
// Iterate over all parameters within the map and assign them to the
// shared network.
for (auto param = element.cbegin(); param != element.cend(); ++param) {
if (param->first == "interface") {
shared_network->setIface(param->second->stringValue());
} else if (param->first == "option-data") {
// Create parser instance for option-data.
CfgOptionPtr cfg_option = shared_network->getCfgOption();
OptionDataListParser parser(AF_INET);
parser.parse(cfg_option, param->second);
} else if (param->first == "subnet4") {
// Create parser instance of subnet4.
Subnets4ListConfigParser parser;
Subnet4Collection subnets;
parser.parse(subnets, param->second);
// Add all returned subnets into shared network.
for (auto subnet = subnets.cbegin(); subnet != subnets.cend();
++subnet) {
shared_network->add(*subnet);
}
}
}
} catch (const std::exception& ex) {
isc_throw(DhcpConfigError, ex.what() << " ("
<< shared_network_data->getPosition() << ")");
}
return (shared_network);
}
SharedNetworkPtr
SharedNetworkParser::parse(const ConstElementPtr& shared_network_data) {
return (SharedNetworkPtr());
SharedNetwork6Ptr
SharedNetwork6Parser::parse(const data::ConstElementPtr& shared_network_data) {
SharedNetwork6Ptr shared_network;
std::string name;
try {
// Shared network is a map.
const auto& element = shared_network_data->mapValue();
// Make sure that the network name has been specified. The name is required
// to create a SharedNetwork6 object.
const auto& name_it = element.find("name");
if (name_it == element.cend()) {
isc_throw(DhcpConfigError, "parameter \"name\" must be specified for"
" a shared network");
}
shared_network.reset(new SharedNetwork6(name_it->second->stringValue()));
// Iterate over all parameters within the map and assign them to the
// shared network.
for (auto param = element.cbegin(); param != element.cend(); ++param) {
if (param->first == "interface") {
shared_network->setIface(param->second->stringValue());
} else if (param->first == "option-data") {
// Create parser instance for option-data.
CfgOptionPtr cfg_option = shared_network->getCfgOption();
OptionDataListParser parser(AF_INET6);
parser.parse(cfg_option, param->second);
} else if (param->first == "subnet6") {
// Create parser instance of subnet6.
Subnets6ListConfigParser parser;
Subnet6Collection subnets;
parser.parse(subnets, param->second);
// Add all returned subnets into shared network.
for (auto subnet = subnets.cbegin(); subnet != subnets.cend();
++subnet) {
shared_network->add(*subnet);
}
}
}
} catch (const std::exception& ex) {
isc_throw(DhcpConfigError, ex.what() << " ("
<< shared_network_data->getPosition() << ")");
}
return (shared_network);
}
} // end of namespace isc::dhcp
......
......@@ -9,19 +9,41 @@
#include <cc/data.h>
#include <cc/simple_parser.h>
#include <dhcpsrv/cfg_subnets4.h>
#include <dhcpsrv/cfg_subnets6.h>
#include <dhcpsrv/shared_network.h>
namespace isc {
namespace dhcp {
class SharedNetworkParser : public isc::data::SimpleParser {
/// @brief Implements parser for IPv4 shared networks.
class SharedNetwork4Parser {
public:
virtual ~SharedNetworkParser();
virtual SharedNetworkPtr
/// @brief Parses shared configuration information for IPv4 shared network.
///
/// @param shared_network_data Data element holding shared network
/// configuration to be parsed.
///
/// @return Pointer to an object representing shared network.
/// @throw DhcpConfigError when shared network configuration is invalid.
SharedNetwork4Ptr
parse(const data::ConstElementPtr& shared_network_data);
};
/// @brief Implements parser for IPv6 shared networks.
class SharedNetwork6Parser {
public:
/// @brief Parses shared configuration information for IPv6 shared network.
///
/// @param shared_network_data Data element holding shared network
/// configuration to be parsed.
///
/// @return Pointer to an object representing shared network.
/// @throw DhcpConfigError when shared network configuration is invalid.
SharedNetwork6Ptr
parse(const data::ConstElementPtr& shared_network_data);
};
} // enf of namespace isc::dhcp
......
......@@ -13,24 +13,69 @@
#include <exceptions/exceptions.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/parsers/shared_network_parser.h>
#include <vector>
namespace isc {
namespace dhcp {
/// @brief Parser for a list of shared networks.
///
/// This is a generic parser for a list of IPv4 or IPv6 shared networks.
///
/// @tparam SharedNetworkParserType Type of the parser to be used for
/// parsing shared network, i.e. @ref SharedNetwork4Parser or
/// @ref SharedNetwork6Parser.
template<typename SharedNetworkParserType>
class SharedNetworksListParser : public data::SimpleParser {
protected:
public:
/// @brief Parses a list of shared networks.
///
/// @param [out] cfg Shared networks configuration structure into which
/// the data should be parsed.
/// @param shared_networks_list_data List element holding a list of
/// shared networks.
/// @tparam Type of the configuration structure into which the result
/// will be stored, i.e. @ref CfgSharedNetworks4 or @ref CfgSharedNetworks6.
///
/// @throw DhcpConfigError when error has occurred, e.g. when networks
/// with duplicated names have been specified.
template<typename CfgSharedNetworksTypePtr>
void parse(const data::ConstElementPtr& shared_networks_list_data,
CfgSharedNetworksTypePtr& cfg) {
if (shared_networks_list_data->getType() != Element::list) {
isc_throw(data::DhcpConfigError, "shared-networks value must be a list");
void parse(CfgSharedNetworksTypePtr& cfg,
const data::ConstElementPtr& shared_networks_list_data) {
try {
// Get the C++ vector holding networks.
const std::vector<data::ElementPtr>& networks_list =
shared_networks_list_data->listValue();
// Iterate over all networks and do the parsing.
for (auto network_element = networks_list.cbegin();
network_element != networks_list.cend(); ++network_element) {
SharedNetworkParserType parser;
auto network = parser.parse(*network_element);
cfg->add(network);
}
} catch (const DhcpConfigError&) {
// Such exceptions are emitted by the lower level parsers and
// errors should already include element's positions. So, we
// simply rethrow.
throw;
} catch (const std::exception& ex) {
// Other exceptions don't include positions of the elements, so
// we should append one.
isc_throw(DhcpConfigError, ex.what() << " ("
<< shared_networks_list_data->getPosition() << ")");
}
}
};
/// @brief Type of the shared networks list parser for IPv4.
typedef SharedNetworksListParser<SharedNetwork4Parser> SharedNetworks4ListParser;
/// @brief Type of the shared networks list parser for IPv6.
typedef SharedNetworksListParser<SharedNetwork6Parser> SharedNetworks6ListParser;
} // end of namespace isc::dhcp
} // end of namespace isc
......
......@@ -123,7 +123,9 @@ if HAVE_CQL
libdhcpsrv_unittests_SOURCES += cql_lease_mgr_unittest.cc
endif
libdhcpsrv_unittests_SOURCES += pool_unittest.cc
libdhcpsrv_unittests_SOURCES += shared_network_parser_unittest.cc
libdhcpsrv_unittests_SOURCES += shared_network_unittest.cc
libdhcpsrv_unittests_SOURCES += shared_networks_list_parser_unittest.cc
libdhcpsrv_unittests_SOURCES += srv_config_unittest.cc
libdhcpsrv_unittests_SOURCES += subnet_unittest.cc
libdhcpsrv_unittests_SOURCES += test_get_callout_handle.cc test_get_callout_handle.h
......
// Copyright (C) 2017 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
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
#include <cc/data.h>
#include <dhcp/dhcp4.h>
#include <dhcp/dhcp6.h>
#include <dhcp/option.h>
#include <dhcp/option4_addrlst.h>
#include <dhcp/option6_addrlst.h>
#include <dhcpsrv/cfg_option.h>
#include <dhcpsrv/parsers/shared_network_parser.h>
#include <gtest/gtest.h>
#include <string>
using namespace isc;
using namespace isc::data;
using namespace isc::dhcp;
namespace {
/// @brief Test fixture class for SharedNetwork4Parser class.
class SharedNetwork4ParserTest : public ::testing::Test {
public:
/// @brief Creates valid shared network configuration.
///
/// @return Valid shared network configuration.
std::string getWorkingConfig() const {
std::string config = "{"
" \"name\": \"bird\","
" \"interface\": \"eth1\","
" \"option-data\": ["
" {"
" \"name\": \"domain-name-servers\","
" \"data\": \"192.0.2.3\""
" }"
" ],"
" \"subnet4\": ["
" {"
" \"id\": 1,"
" \"subnet\": \"10.1.2.0/24\","
" \"interface\": \"\","
" \"renew-timer\": 100,"
" \"rebind-timer\": 200,"
" \"valid-lifetime\": 300,"
" \"match-client-id\": false,"
" \"next-server\": \"\","
" \"client-class\": \"\","
" \"reservation-mode\": \"all\","
" \"4o6-interface\": \"\","
" \"4o6-interface-id\": \"\","
" \"4o6-subnet\": \"\","
" \"dhcp4o6-port\": 0,"
" \"decline-probation-period\": 86400,"
" \"reservation-mode\": \"all\""
" },"
" {"
" \"id\": 2,"
" \"subnet\": \"192.0.2.0/24\","
" \"interface\": \"\","
" \"renew-timer\": 10,"
" \"rebind-timer\": 20,"
" \"valid-lifetime\": 30,"
" \"match-client-id\": false,"
" \"next-server\": \"\","
" \"client-class\": \"\","
" \"reservation-mode\": \"all\","
" \"4o6-interface\": \"\","
" \"4o6-interface-id\": \"\","
" \"4o6-subnet\": \"\","
" \"dhcp4o6-port\": 0,"
" \"decline-probation-period\": 86400,"
" \"reservation-mode\": \"all\""
" }"
" ]"
"}";
return (config);
}
};
// This test verifies that shared network parser for IPv4 works properly
// in a positive test scenario.
TEST_F(SharedNetwork4ParserTest, parse) {
// Basic configuration for shared network. A bunch of parameters
// have to be specified for subnets because subnet parsers expect
// that default and global values are set.
std::string config = getWorkingConfig();
ElementPtr config_element = Element::fromJSON(config);
// Parse configuration specified above.
SharedNetwork4Parser parser;
SharedNetwork4Ptr network;
ASSERT_NO_THROW(network = parser.parse(config_element));
ASSERT_TRUE(network);
// Check basic parameters.
EXPECT_EQ("bird", network->getName());
EXPECT_EQ("eth1", network->getIface());
// Subnet with id 1
Subnet4Ptr subnet1 = network->getSubnet(SubnetID(1));
ASSERT_TRUE(subnet1);
EXPECT_EQ("10.1.2.0", subnet1->get().first.toText());
// Subnet with id 2
Subnet4Ptr subnet2 = network->getSubnet(SubnetID(2));
ASSERT_TRUE(subnet2);
EXPECT_EQ("192.0.2.0", subnet2->get().first.toText());
// DHCP options
ConstCfgOptionPtr cfg_option = network->getCfgOption();
ASSERT_TRUE(cfg_option);
OptionDescriptor opt_dns_servers = cfg_option->get("dhcp4",
DHO_DOMAIN_NAME_SERVERS);
ASSERT_TRUE(opt_dns_servers.option_);
Option4AddrLstPtr dns_servers = boost::dynamic_pointer_cast<
Option4AddrLst>(opt_dns_servers.option_);
ASSERT_TRUE(dns_servers);
Option4AddrLst::AddressContainer addresses = dns_servers->getAddresses();
ASSERT_EQ(1, addresses.size());
EXPECT_EQ("192.0.2.3", addresses[0].toText());
}
// This test verifies that parser throws an exception when mandatory parameter
// "name" is not specified.
TEST_F(SharedNetwork4ParserTest, missingName) {
// Remove a name parameter from the valid configuration.
std::string config = getWorkingConfig();
ElementPtr config_element = Element::fromJSON(config);
ASSERT_NO_THROW(config_element->remove("name"));
// Parse configuration specified above.
SharedNetwork4Parser parser;
SharedNetwork4Ptr network;
ASSERT_THROW(network = parser.parse(config_element), DhcpConfigError);
}
/// @brief Test fixture class for SharedNetwork6Parser class.
class SharedNetwork6ParserTest : public ::testing::Test {
public:
/// @brief Creates valid shared network configuration.
///
/// @return Valid shared network configuration.
std::string getWorkingConfig() const {
std::string config = "{"
" \"name\": \"bird\","
" \"interface\": \"eth1\","
" \"option-data\": ["
" {"
" \"name\": \"dns-servers\","
" \"data\": \"2001:db8:1::cafe\""
" }"
" ],"
" \"subnet6\": ["
" {"
" \"id\": 1,"
" \"subnet\": \"3000::/16\","
" \"interface\": \"\","
" \"interface-id\": \"\","
" \"renew-timer\": 100,"
" \"rebind-timer\": 200,"
" \"preferred-lifetime\": 300,"
" \"valid-lifetime\": 400,"
" \"client-class\": \"\","
" \"reservation-mode\": \"all\","
" \"decline-probation-period\": 86400,"
" \"dhcp4o6-port\": 0,"
" \"rapid-commit\": false"
" },"
" {"
" \"id\": 2,"
" \"subnet\": \"2001:db8:1::/64\","
" \"interface\": \"\","
" \"interface-id\": \"\","
" \"renew-timer\": 10,"
" \"rebind-timer\": 20,"
" \"preferred-lifetime\": 30,"
" \"valid-lifetime\": 40,"
" \"client-class\": \"\","
" \"reservation-mode\": \"all\","
" \"decline-probation-period\": 86400,"
" \"dhcp4o6-port\": 0,"
" \"rapid-commit\": false"
" }"
" ]"
"}";
return (config);
}
};
// This test verifies that shared network parser for IPv4 works properly
// in a positive test scenario.
TEST_F(SharedNetwork6ParserTest, parse) {
// Basic configuration for shared network. A bunch of parameters
// have to be specified for subnets because subnet parsers expect
// that default and global values are set.
std::string config = getWorkingConfig();
ElementPtr config_element = Element::fromJSON(config);
// Parse configuration specified above.
SharedNetwork6Parser parser;
SharedNetwork6Ptr network;
ASSERT_NO_THROW(network = parser.parse(config_element));
ASSERT_TRUE(network);
// Check basic parameters.
EXPECT_EQ("bird", network->getName());
EXPECT_EQ("eth1", network->getIface());
// Subnet with id 1
Subnet6Ptr subnet1 = network->getSubnet(SubnetID(1));