Parcourir la source

[5305] Parsers for shared networks added.

Marcin Siodelski il y a 7 ans
Parent
commit
b0cca7982a

+ 13 - 0
src/lib/dhcpsrv/cfg_shared_networks.h

@@ -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.

+ 41 - 0
src/lib/dhcpsrv/parsers/dhcp_parsers.cc

@@ -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 **********************
 

+ 17 - 0
src/lib/dhcpsrv/parsers/dhcp_parsers.h

@@ -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

+ 1 - 0
src/lib/dhcpsrv/parsers/option_data_parser.h

@@ -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>

+ 105 - 4
src/lib/dhcpsrv/parsers/shared_network_parser.cc

@@ -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

+ 26 - 4
src/lib/dhcpsrv/parsers/shared_network_parser.h

@@ -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

+ 51 - 6
src/lib/dhcpsrv/parsers/shared_networks_list_parser.h

@@ -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
 

+ 2 - 0
src/lib/dhcpsrv/tests/Makefile.am

@@ -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

+ 242 - 0
src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc

@@ -0,0 +1,242 @@
+// 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));
+    ASSERT_TRUE(subnet1);
+    EXPECT_EQ("3000::", subnet1->get().first.toText());
+
+    // Subnet with id 2
+    Subnet6Ptr subnet2 = network->getSubnet(SubnetID(2));
+    ASSERT_TRUE(subnet2);
+    EXPECT_EQ("2001:db8:1::", subnet2->get().first.toText());
+
+    // DHCP options
+    ConstCfgOptionPtr cfg_option = network->getCfgOption();
+    ASSERT_TRUE(cfg_option);
+    OptionDescriptor opt_dns_servers = cfg_option->get("dhcp6",
+                                                       D6O_NAME_SERVERS);
+    ASSERT_TRUE(opt_dns_servers.option_);
+    Option6AddrLstPtr dns_servers = boost::dynamic_pointer_cast<
+        Option6AddrLst>(opt_dns_servers.option_);
+    ASSERT_TRUE(dns_servers);
+    Option6AddrLst::AddressContainer addresses = dns_servers->getAddresses();
+    ASSERT_EQ(1, addresses.size());
+    EXPECT_EQ("2001:db8:1::cafe", addresses[0].toText());
+}
+
+
+} // end of anonymous namespace

+ 75 - 0
src/lib/dhcpsrv/tests/shared_networks_list_parser_unittest.cc

@@ -0,0 +1,75 @@
+// 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 <dhcpsrv/cfg_shared_networks.h>
+#include <dhcpsrv/shared_network.h>
+#include <dhcpsrv/parsers/shared_networks_list_parser.h>
+#include <gtest/gtest.h>
+#include <string>
+
+using namespace isc;
+using namespace isc::data;
+using namespace isc::dhcp;
+
+namespace {
+
+// This is a basic test verifying that all shared networks are correctly
+// parsed.
+TEST(SharedNetworkListParserTest, parse) {
+    // Basic configuration with array of shared networks.
+    std::string config = "["
+        "    {"
+        "        \"name\": \"bird\","
+        "        \"interface\": \"eth0\""
+        "    },"
+        "    {"
+        "        \"name\": \"monkey\","
+        "        \"interface\": \"eth1\""
+        "    }"
+        "]";
+
+    ElementPtr config_element = Element::fromJSON(config);
+
+    SharedNetworks4ListParser parser;
+    CfgSharedNetworks4Ptr cfg(new CfgSharedNetworks4());;
+    ASSERT_NO_THROW(parser.parse(cfg, config_element));
+
+    SharedNetwork4Ptr network1 = cfg->getByName("bird");
+    ASSERT_TRUE(network1);
+    EXPECT_EQ("bird", network1->getName());
+    EXPECT_EQ("eth0", network1->getIface());
+
+    SharedNetwork4Ptr network2 = cfg->getByName("monkey");
+    ASSERT_TRUE(network2);
+    EXPECT_EQ("monkey", network2->getName());
+    EXPECT_EQ("eth1", network2->getIface());
+}
+
+// This test verifies that specifying two networks with the same name
+// yields an error.
+TEST(SharedNetworkListParserTest, duplicatedName) {
+    // Basic configuration with two networks having the same name.
+    std::string config = "["
+        "    {"
+        "        \"name\": \"bird\","
+        "        \"interface\": \"eth0\""
+        "    },"
+        "    {"
+        "        \"name\": \"bird\","
+        "        \"interface\": \"eth1\""
+        "    }"
+        "]";
+
+    ElementPtr config_element = Element::fromJSON(config);
+
+    SharedNetworks4ListParser parser;
+    CfgSharedNetworks4Ptr cfg(new CfgSharedNetworks4());;
+    EXPECT_THROW(parser.parse(cfg, config_element), DhcpConfigError);
+}
+
+} // end of anonymous namespace