Parcourir la source

[master] Merge branch 'trac3464' (pool definitions are now structures)

Conflicts:
	ChangeLog
	doc/examples/kea4/several-subnets.json
	doc/examples/kea4/single-subnet.json
	doc/examples/kea6/several-subnets.json
	doc/guide/dhcp4-srv.xml
Tomek Mrugalski il y a 10 ans
Parent
commit
f73452e8ea

+ 6 - 0
ChangeLog

@@ -1,3 +1,9 @@
+815.	[func]		tomek
+	Pool definitions in DHCPv4 and DHCPv6 are now lists of
+	structures. This makes adding new per-pool parameters easier in
+	the future.
+	(Trac #3464, git 4bd0c0eda9d86608f8802d28bd360239fe88e905)
+
 814.	[func,doc]	tomek
 	It is now possible to specify logging parameters in a
 	configuration file for DHCPv4, DHCPv6 and DHCP-DDNS components.

+ 4 - 4
doc/examples/kea4/several-subnets.json

@@ -26,12 +26,12 @@
 
 # The following list defines subnets. Each subnet consists of at
 # least subnet and pool entries.
-  "subnet4": [
-  {    "pool": [ "192.0.2.1 - 192.0.2.200" ],
+  "subnet4": [ 
+  {    "pools": [ { "pool":  "192.0.2.1 - 192.0.2.200" } ],
        "subnet": "192.0.2.0/24"  },
-  {    "pool": [ "192.0.3.100 - 192.0.3.200" ],
+  {    "pools": [ { "pool": "192.0.3.100 - 192.0.3.200" } ],
        "subnet": "192.0.3.0/24"  },
-  {    "pool": [ "192.0.4.1 - 192.0.4.254" ],
+  {    "pools": [ { "pool": "192.0.4.1 - 192.0.4.254" } ],
        "subnet": "192.0.4.0/24"  } ]
 },
 

+ 5 - 2
doc/examples/kea4/single-subnet.json

@@ -32,9 +32,12 @@
 # The following list defines subnets. We have only one subnet
 # here. We tell Kea that it is directly available over local interface.
   "subnet4": [
-  {    "pool": [ "192.0.2.1 - 192.0.2.200" ],
+    {
+       "pools": [ { "pool":  "192.0.2.1 - 192.0.2.200" } ],
        "subnet": "192.0.2.0/24",
-       "interface": "eth0"  } ]
+       "interface": "eth0"
+    } 
+  ]
 },
 
 # The following configures logging. It assumes that messages with at least

+ 6 - 6
doc/examples/kea6/several-subnets.json

@@ -28,14 +28,14 @@
 
 # The following list defines subnets. Each subnet consists of at
 # least subnet and pool entries.
-  "subnet6": [
-  {    "pool": [ "2001:db8:1::/80" ],
+  "subnet6": [ 
+  {    "pools": [ { "pool": "2001:db8:1::/80" } ],
        "subnet": "2001:db8:1::/64"  },
-  {    "pool": [ "2001:db8:2::/80" ],
-       "subnet": "2001:db8:2::/64"  },
-  {    "pool": [ "2001:db8:3::/80" ],
+  {    "pools": [ { "pool": "2001:db8:2::/80" } ],
+       "subnet": "2001:db8:2::/64"  }, 
+  {    "pools": [ { "pool": "2001:db8:3::/80" } ],
        "subnet": "2001:db8:3::/64"  },
-  {    "pool": [ "2001:db8:4::/80" ],
+  {    "pools": [ { "pool": "2001:db8:4::/80" } ],
        "subnet": "2001:db8:4::/64"  } ]
 },
 

+ 30 - 15
doc/guide/dhcp4-srv.xml

@@ -97,7 +97,9 @@
     "subnet4": [
         {
             "subnet": "192.0.2.0/24",
-            "pool": [ "192.0.2.1 - 192.0.2.200" ]
+            "pools": [
+                { "pool": "192.0.2.1 - 192.0.2.200" }
+            ]
         }
     ]
 
@@ -183,7 +185,7 @@ so it starts and ends with square brackets.  Each subnet definition in
 the list has several attributes associated with it, so is a structure
 and is opened and closed with braces. At minimum, a subnet definition
 has to have at least two parameters: <command>subnet</command> (that
-defines the whole subnet) and <command>pool</command> (which is a list of
+defines the whole subnet) and <command>pools</command> (which is a list of
 dynamically allocated pools that are governed by the DHCP server).</para>
 
 <para>The example contains a single subnet. Had more than one been defined,
@@ -194,15 +196,15 @@ syntax would be used:
 <screen>
 "subnet4": [
     {
-        "pool": [ "192.0.2.1 - 192.0.2.200" ],
+        "pools": [ { "pool":  "192.0.2.1 - 192.0.2.200" } ],
         "subnet": "192.0.2.0/24"
     },
     {
-        "pool": [ "192.0.3.100 - 192.0.3.200" ],
+        "pools": [ { "pool": "192.0.3.100 - 192.0.3.200" } ],
         "subnet": "192.0.3.0/24"
     },
     {
-        "pool": [ "192.0.4.1 - 192.0.4.254" ],
+        "pool": [ { "pool": "192.0.4.1 - 192.0.4.254" } ],
         "subnet": "192.0.4.0/24"
     }
 ]
@@ -392,16 +394,26 @@ temporarily override a list of interface names and listen on all interfaces.
 "Dhcp4": {
     <userinput>"subnet4": [
         "subnet": "192.0.2.0/24",
-        "pool": [ "192.0.2.10 - 192.0.2.20" ]</userinput>,
+        "pools": [
+	    {
+	        "pool": "192.0.2.10 - 192.0.2.20"
+            }
+        ]</userinput>,
         ...
     ]
 }</screen>
 
-    Note that subnet is defined as a simple string, but the pool parameter is
+    Note that subnet is defined as a simple string, but the 'pools' parameter is
     actually a list of pools: for this reason, the pool definition is enclosed
     in square brackets, even though only one range of addresses is
     specified in this example.</para>
 
+    <para>Each <command>pool</command> is a structure that contains the parameters
+    th describe a single pool. Currently there is only one parameter,
+    <command>pool</command>, which gives the range of addresses
+    in the pool. Additional parameters will be added in future
+    releases of Kea.</para>
+
     <para>It is possible to define more than one pool in a subnet: continuing
     the previous example, further assume that 192.0.2.64/26 should be also be
     managed by the server. It could be written as 192.0.2.64 to
@@ -411,7 +423,10 @@ temporarily override a list of interface names and listen on all interfaces.
 <screen>
 "Dhcp4": {
     "subnet4": [
-        <userinput>"pool": [ "192.0.2.10-192.0.2.20", "192.0.2.64/26" ]</userinput>,
+        <userinput>"pools": [
+            { "pool": "192.0.2.10-192.0.2.20" },
+            { "pool": "192.0.2.64/26" }
+        ]</userinput>,
         ...
     ],
     ...
@@ -428,17 +443,17 @@ temporarily override a list of interface names and listen on all interfaces.
     "subnet4": [
         {
             "subnet": "192.0.2.0/24",
-            "pool": [ "192.0.2.1 - 192.0.2.200" ],
+            "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
             ...
         },
         {
             "subnet": "192.0.3.0/24",
-            "pool": [ "192.0.3.100 - 192.0.3.200" ],
+            "pools": [ { "pool": "192.0.3.100 - 192.0.3.200" } ],
             ...
         },
         {
             "subnet": "192.0.4.0/24",
-            "pool": [ "192.0.4.1 - 192.0.4.254" ],
+            "pools": [ { "pool": "192.0.4.1 - 192.0.4.254" } ],
             ...
         }
     ]
@@ -1198,7 +1213,7 @@ temporarily override a list of interface names and listen on all interfaces.
     "subnet4": [
         {
             <userinput>subnet: "192.0.2.0/24",
-            "pool": [ "192.0.2.10 - 192.0.2.20" ],
+            "pools": [ { "pool": "192.0.2.10 - 192.0.2.20" } ],
             "client-class": "VENDOR_CLASS_docsis3.0"</userinput>
         }
     ],
@@ -1709,7 +1724,7 @@ temporarily override a list of interface names and listen on all interfaces.
     "subnet4: [
         {
             "subnet": "192.0.2.0/24",
-            "pool": [ "192.0.2.10 - 192.0.2.20" ],
+            "pools": [ { "pool": "192.0.2.10 - 192.0.2.20" } ],
             <userinput>"relay": {
                 "ip-address": "10.0.0.1"
             }</userinput>,
@@ -1745,7 +1760,7 @@ temporarily override a list of interface names and listen on all interfaces.
     "subnet4: [
         {
             "subnet": "10.1.1.0/24",
-            "pool":  [ "10.1.1.2 - 10.1.1.20" ],
+            "pools":  [ { "pool": "10.1.1.2 - 10.1.1.20" } ],
             <userinput>"client-class" "docsis3.0",
             "relay": {
                 "ip-address": "10.1.1.1"
@@ -1753,7 +1768,7 @@ temporarily override a list of interface names and listen on all interfaces.
         },
         {
             "subnet": "192.0.2.0/24",
-            "pool": [ "192.0.2.10 - 192.0.2.20" ],
+            "pools": [ { "pool": "192.0.2.10 - 192.0.2.20" } ],
             <userinput>"relay": {
                 "ip-address": "10.1.1.1"
             }</userinput>

+ 7 - 0
doc/guide/dhcp6-srv.xml

@@ -456,6 +456,13 @@ temporarily override a list of interface names and listen on all interfaces.
         is actually a list of pools: for this reason, the pool definition is
         enclosed in square brackets, even though only one range of addresses
         is specified.</para>
+
+        <para>Each <command>pool</command> is a structure that contains the
+        parameters th describe a single pool. Currently there is only one
+        parameter, <command>pool</command>, which gives the range of addresses
+        in the pool. Additional parameters will be added in future releases of
+        Kea.</para>
+
         <para>It is possible to define more than one pool in a
         subnet: continuing the previous example, further assume that
         2001:db8:1:0:5::/80 should be also be managed by the server. It could be written as

+ 14 - 2
src/bin/dhcp4/json_config_parser.cc

@@ -152,6 +152,18 @@ protected:
     }
 };
 
+class Pools4ListParser : public PoolsListParser {
+public:
+    Pools4ListParser(const std::string& dummy, PoolStoragePtr pools)
+        :PoolsListParser(dummy, pools) {
+    }
+
+protected:
+    virtual ParserPtr poolParserMaker(PoolStoragePtr storage) {
+        return (ParserPtr(new Pool4Parser("pool", storage)));
+    }
+};
+
 /// @brief This class parses a single IPv4 subnet.
 ///
 /// This is the IPv4 derivation of the SubnetConfigParser class and it parses
@@ -227,8 +239,8 @@ protected:
                    (config_id.compare("client-class") == 0) ||
                    (config_id.compare("next-server") == 0)) {
             parser = new StringParser(config_id, string_values_);
-        } else if (config_id.compare("pool") == 0) {
-            parser = new Pool4Parser(config_id, pools_);
+        } else if (config_id.compare("pools") == 0) {
+            parser = new Pools4ListParser(config_id, pools_);
         } else if (config_id.compare("relay") == 0) {
             parser = new RelayInfoParser(config_id, relay_info_, Option::V4);
         } else if (config_id.compare("option-data") == 0) {

+ 111 - 54
src/bin/dhcp4/tests/config_parser_unittest.cc

@@ -184,7 +184,7 @@ public:
             "\"rebind-timer\": 2000, "
             "\"renew-timer\": 1000, "
             "\"subnet4\": [ { "
-            "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+            "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
             "    \"subnet\": \"192.0.2.0/24\", "
             "    \"option-data\": [ {";
         bool first = true;
@@ -519,7 +519,7 @@ TEST_F(Dhcp4ParserTest, unspecifiedRenewTimer) {
     string config = "{ \"interfaces\": [ \"*\" ],"
         "\"rebind-timer\": 2000, "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\" } ],"
         "\"valid-lifetime\": 4000 }";
 
@@ -553,7 +553,7 @@ TEST_F(Dhcp4ParserTest, unspecifiedRebindTimer) {
     string config = "{ \"interfaces\": [ \"*\" ],"
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\" } ],"
         "\"valid-lifetime\": 4000 }";
 
@@ -588,7 +588,7 @@ TEST_F(Dhcp4ParserTest, subnetGlobalDefaults) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\" } ],"
         "\"valid-lifetime\": 4000 }";
 
@@ -623,20 +623,20 @@ TEST_F(Dhcp4ParserTest, multipleSubnets) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\" "
         " },"
         " {"
-        "    \"pool\": [ \"192.0.3.101 - 192.0.3.150\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.3.101 - 192.0.3.150\" } ],"
         "    \"subnet\": \"192.0.3.0/24\", "
         "    \"id\": 0 "
         " },"
         " {"
-        "    \"pool\": [ \"192.0.4.101 - 192.0.4.150\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.4.101 - 192.0.4.150\" } ],"
         "    \"subnet\": \"192.0.4.0/24\" "
         " },"
         " {"
-        "    \"pool\": [ \"192.0.5.101 - 192.0.5.150\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.5.101 - 192.0.5.150\" } ],"
         "    \"subnet\": \"192.0.5.0/24\" "
         " } ],"
         "\"valid-lifetime\": 4000 }";
@@ -674,22 +674,22 @@ TEST_F(Dhcp4ParserTest, multipleSubnetsExplicitIDs) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\", "
         "    \"id\": 1024 "
         " },"
         " {"
-        "    \"pool\": [ \"192.0.3.101 - 192.0.3.150\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.3.101 - 192.0.3.150\" } ],"
         "    \"subnet\": \"192.0.3.0/24\", "
         "    \"id\": 100 "
         " },"
         " {"
-        "    \"pool\": [ \"192.0.4.101 - 192.0.4.150\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.4.101 - 192.0.4.150\" } ],"
         "    \"subnet\": \"192.0.4.0/24\", "
         "    \"id\": 1 "
         " },"
         " {"
-        "    \"pool\": [ \"192.0.5.101 - 192.0.5.150\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.5.101 - 192.0.5.150\" } ],"
         "    \"subnet\": \"192.0.5.0/24\", "
         "    \"id\": 34 "
         " } ],"
@@ -725,22 +725,22 @@ TEST_F(Dhcp4ParserTest, multipleSubnetsOverlapingIDs) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\", "
         "    \"id\": 1024 "
         " },"
         " {"
-        "    \"pool\": [ \"192.0.3.101 - 192.0.3.150\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.3.101 - 192.0.3.150\" } ],"
         "    \"subnet\": \"192.0.3.0/24\", "
         "    \"id\": 100 "
         " },"
         " {"
-        "    \"pool\": [ \"192.0.4.101 - 192.0.4.150\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.4.101 - 192.0.4.150\" } ],"
         "    \"subnet\": \"192.0.4.0/24\", "
         "    \"id\": 1024 "
         " },"
         " {"
-        "    \"pool\": [ \"192.0.5.101 - 192.0.5.150\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.5.101 - 192.0.5.150\" } ],"
         "    \"subnet\": \"192.0.5.0/24\", "
         "    \"id\": 34 "
         " } ],"
@@ -763,22 +763,22 @@ TEST_F(Dhcp4ParserTest, reconfigureRemoveSubnet) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\", "
         "    \"id\": 1 "
         " },"
         " {"
-        "    \"pool\": [ \"192.0.3.101 - 192.0.3.150\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.3.101 - 192.0.3.150\" } ],"
         "    \"subnet\": \"192.0.3.0/24\", "
         "    \"id\": 2 "
         " },"
         " {"
-        "    \"pool\": [ \"192.0.4.101 - 192.0.4.150\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.4.101 - 192.0.4.150\" } ],"
         "    \"subnet\": \"192.0.4.0/24\", "
         "    \"id\": 3 "
         " },"
         " {"
-        "    \"pool\": [ \"192.0.5.101 - 192.0.5.150\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.5.101 - 192.0.5.150\" } ],"
         "    \"subnet\": \"192.0.5.0/24\", "
         "    \"id\": 4 "
         " } ],"
@@ -789,17 +789,17 @@ TEST_F(Dhcp4ParserTest, reconfigureRemoveSubnet) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\", "
         "    \"id\": 1 "
         " },"
         " {"
-        "    \"pool\": [ \"192.0.3.101 - 192.0.3.150\" ],"
+        "    \"pools\": [ { \"pool\":  \"192.0.3.101 - 192.0.3.150\" } ],"
         "    \"subnet\": \"192.0.3.0/24\", "
         "    \"id\": 2 "
         " },"
         " {"
-        "    \"pool\": [ \"192.0.4.101 - 192.0.4.150\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.4.101 - 192.0.4.150\" } ],"
         "    \"subnet\": \"192.0.4.0/24\", "
         "    \"id\": 3 "
         " } ],"
@@ -810,17 +810,17 @@ TEST_F(Dhcp4ParserTest, reconfigureRemoveSubnet) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\", "
         "    \"id\": 1 "
         " },"
         " {"
-        "    \"pool\": [ \"192.0.4.101 - 192.0.4.150\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.4.101 - 192.0.4.150\" } ],"
         "    \"subnet\": \"192.0.4.0/24\", "
         "    \"id\": 3 "
         " },"
         " {"
-        "    \"pool\": [ \"192.0.5.101 - 192.0.5.150\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.5.101 - 192.0.5.150\" } ],"
         "    \"subnet\": \"192.0.5.0/24\", "
         "    \"id\": 4 "
         " } ],"
@@ -888,7 +888,7 @@ TEST_F(Dhcp4ParserTest, nextServerGlobal) {
         "\"renew-timer\": 1000, "
         "\"next-server\": \"1.2.3.4\", "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\" } ],"
         "\"valid-lifetime\": 4000 }";
 
@@ -917,7 +917,7 @@ TEST_F(Dhcp4ParserTest, nextServerSubnet) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"next-server\": \"1.2.3.4\", "
         "    \"subnet\": \"192.0.2.0/24\" } ],"
         "\"valid-lifetime\": 4000 }";
@@ -948,7 +948,7 @@ TEST_F(Dhcp4ParserTest, nextServerNegative) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"rebind-timer\": 2000, "
         "    \"renew-timer\": 1000, "
         "    \"next-server\": \"a.b.c.d\", "
@@ -960,7 +960,7 @@ TEST_F(Dhcp4ParserTest, nextServerNegative) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"rebind-timer\": 2000, "
         "    \"renew-timer\": 1000, "
         "    \"next-server\": \"2001:db8::1\", "
@@ -972,7 +972,7 @@ TEST_F(Dhcp4ParserTest, nextServerNegative) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"rebind-timer\": 2000, "
         "    \"renew-timer\": 1000, "
         "    \"next-server\": \"\", "
@@ -1008,7 +1008,7 @@ TEST_F(Dhcp4ParserTest, nextServerOverride) {
         "\"renew-timer\": 1000, "
         "\"next-server\": \"192.0.0.1\", "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"next-server\": \"1.2.3.4\", "
         "    \"subnet\": \"192.0.2.0/24\" } ],"
         "\"valid-lifetime\": 4000 }";
@@ -1038,7 +1038,7 @@ TEST_F(Dhcp4ParserTest, echoClientId) {
         "\"renew-timer\": 1000, "
         "\"echo-client-id\": false,"
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\" } ],"
         "\"valid-lifetime\": 4000 }";
 
@@ -1047,7 +1047,7 @@ TEST_F(Dhcp4ParserTest, echoClientId) {
         "\"renew-timer\": 1000, "
         "\"echo-client-id\": true,"
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\":  \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\" } ],"
         "\"valid-lifetime\": 4000 }";
 
@@ -1079,7 +1079,7 @@ TEST_F(Dhcp4ParserTest, subnetLocal) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"renew-timer\": 1, "
         "    \"rebind-timer\": 2, "
         "    \"valid-lifetime\": 4,"
@@ -1101,6 +1101,63 @@ TEST_F(Dhcp4ParserTest, subnetLocal) {
     EXPECT_EQ(4, subnet->getValid());
 }
 
+// This test checks that multiple pools can be defined and handled properly.
+// The test defines 2 subnets, each with 2 pools.
+TEST_F(Dhcp4ParserTest, multiplePools) {
+
+    // Collection with two subnets, each with 2 pools.
+    string config = "{ \"interfaces\": [ \"*\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pools\": [ "
+        "        { \"pool\": \"192.0.2.0/28\" },"
+        "        { \"pool\": \"192.0.2.200-192.0.2.255\" }"
+        "    ],"
+        "    \"subnet\": \"192.0.2.0/24\" "
+        " },"
+        " {"
+        "    \"pools\": [ "
+        "    { \"pool\": \"192.0.3.0/25\" },"
+        "    { \"pool\": \"192.0.3.128/25\" }"
+        "    ],"
+        "    \"subnet\": \"192.0.3.0/24\""
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+    ElementPtr json;
+    ASSERT_NO_THROW(json = Element::fromJSON(config));
+
+    ConstElementPtr status;
+    ASSERT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+    checkResult(status, 0);
+
+    const Subnet4Collection* subnets = CfgMgr::instance().getSubnets4();
+    ASSERT_TRUE(subnets);
+    ASSERT_EQ(2, subnets->size()); // We expect 2 subnets
+
+    // Check the first subnet
+    const PoolCollection& pools1 = subnets->at(0)->getPools(Lease::TYPE_V4);
+    ASSERT_EQ(2, pools1.size());
+    EXPECT_EQ("type=V4, 192.0.2.0-192.0.2.15",
+              pools1[0]->toText());
+    EXPECT_EQ("type=V4, 192.0.2.200-192.0.2.255",
+              pools1[1]->toText());
+    // There shouldn't be any TA or PD pools
+    EXPECT_THROW(subnets->at(0)->getPools(Lease::TYPE_TA), BadValue);
+    EXPECT_THROW(subnets->at(0)->getPools(Lease::TYPE_PD), BadValue);
+
+    // Check the second subnet
+    const PoolCollection& pools2 = subnets->at(1)->getPools(Lease::TYPE_V4);
+    ASSERT_EQ(2, pools2.size());
+    EXPECT_EQ("type=V4, 192.0.3.0-192.0.3.127",
+              pools2[0]->toText());
+    EXPECT_EQ("type=V4, 192.0.3.128-192.0.3.255",
+              pools2[1]->toText());
+    // There shouldn't be any TA or PD pools
+    EXPECT_THROW(subnets->at(0)->getPools(Lease::TYPE_TA).empty(), BadValue);
+    EXPECT_THROW(subnets->at(0)->getPools(Lease::TYPE_PD).empty(), BadValue);
+}
+
 // Test verifies that a subnet with pool values that do not belong to that
 // pool are rejected.
 TEST_F(Dhcp4ParserTest, poolOutOfSubnet) {
@@ -1111,7 +1168,7 @@ TEST_F(Dhcp4ParserTest, poolOutOfSubnet) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.4.0/28\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.4.0/28\" } ],"
         "    \"subnet\": \"192.0.2.0/24\" } ],"
         "\"valid-lifetime\": 4000 }";
 
@@ -1136,7 +1193,7 @@ TEST_F(Dhcp4ParserTest, poolPrefixLen) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.128/28\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.128/28\" } ],"
         "    \"subnet\": \"192.0.2.0/24\" } ],"
         "\"valid-lifetime\": 4000 }";
 
@@ -1713,7 +1770,7 @@ TEST_F(Dhcp4ParserTest, optionDataDefaults) {
         "    \"csv-format\": False"
         " } ],"
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\""
         " } ],"
         "\"valid-lifetime\": 4000 }";
@@ -1795,7 +1852,7 @@ TEST_F(Dhcp4ParserTest, optionDataTwoSpaces) {
         "    \"encapsulate\": \"\""
         " } ],"
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\""
         " } ]"
         "}";
@@ -1949,7 +2006,7 @@ TEST_F(Dhcp4ParserTest, optionDataEncapsulate) {
         "    \"encapsulate\": \"\""
         " } ],"
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\""
         " } ]"
         "}";
@@ -2004,7 +2061,7 @@ TEST_F(Dhcp4ParserTest, optionDataInSingleSubnet) {
         "      \"csv-format\": False"
         " } ],"
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\", "
         "    \"option-data\": [ {"
         "          \"name\": \"dhcp-message\","
@@ -2151,7 +2208,7 @@ TEST_F(Dhcp4ParserTest, optionDataInMultipleSubnets) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\":  \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\", "
         "    \"option-data\": [ {"
         "          \"name\": \"dhcp-message\","
@@ -2162,7 +2219,7 @@ TEST_F(Dhcp4ParserTest, optionDataInMultipleSubnets) {
         "        } ]"
         " },"
         " {"
-        "    \"pool\": [ \"192.0.3.101 - 192.0.3.150\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.3.101 - 192.0.3.150\" } ],"
         "    \"subnet\": \"192.0.3.0/24\", "
         "    \"option-data\": [ {"
         "          \"name\": \"default-ip-ttl\","
@@ -2518,7 +2575,7 @@ TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) {
         "    \"encapsulate\": \"\""
         " } ],"
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\""
         " } ]"
         "}";
@@ -2601,7 +2658,7 @@ TEST_F(Dhcp4ParserTest, vendorOptionsHex) {
         "    \"csv-format\": False"
         " } ],"
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1-192.0.2.10\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1-192.0.2.10\" } ],"
         "    \"subnet\": \"192.0.2.0/24\""
         " } ]"
         "}";
@@ -2662,7 +2719,7 @@ TEST_F(Dhcp4ParserTest, vendorOptionsCsv) {
         "    \"encapsulate\": \"\""
         " } ],"
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\":  \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\" "
         " } ]"
         "}";
@@ -2746,7 +2803,7 @@ buildHooksLibrariesConfig(const std::vector<std::string>& libraries) {
         "    \"encapsulate\": \"\""
         " } ],"
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\""
         " } ]"
         "}");
@@ -2914,7 +2971,7 @@ TEST_F(Dhcp4ParserTest, d2ClientConfig) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\" } ],"
         " \"dhcp-ddns\" : {"
         "     \"enable-updates\" : true, "
@@ -2979,7 +3036,7 @@ TEST_F(Dhcp4ParserTest, invalidD2ClientConfig) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\" } ],"
         " \"dhcp-ddns\" : {"
         "     \"enable-updates\" : true, "
@@ -3025,7 +3082,7 @@ TEST_F(Dhcp4ParserTest, subnetRelayInfo) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"renew-timer\": 1, "
         "    \"rebind-timer\": 2, "
         "    \"valid-lifetime\": 4,"
@@ -3056,22 +3113,22 @@ TEST_F(Dhcp4ParserTest, classifySubnets) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\", "
         "    \"client-class\": \"alpha\" "
         " },"
         " {"
-        "    \"pool\": [ \"192.0.3.101 - 192.0.3.150\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.3.101 - 192.0.3.150\" } ],"
         "    \"subnet\": \"192.0.3.0/24\", "
         "    \"client-class\": \"beta\" "
         " },"
         " {"
-        "    \"pool\": [ \"192.0.4.101 - 192.0.4.150\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.4.101 - 192.0.4.150\" } ],"
         "    \"subnet\": \"192.0.4.0/24\", "
         "    \"client-class\": \"gamma\" "
         " },"
         " {"
-        "    \"pool\": [ \"192.0.5.101 - 192.0.5.150\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.5.101 - 192.0.5.150\" } ],"
         "    \"subnet\": \"192.0.5.0/24\" "
         " } ],"
         "\"valid-lifetime\": 4000 }";

+ 1 - 1
src/bin/dhcp4/tests/d2_unittest.cc

@@ -99,7 +99,7 @@ Dhcp4SrvD2Test::configureD2(bool enable_d2, const bool exp_result,
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\" } ],"
         " \"dhcp-ddns\" : {"
         "     \"enable-updates\" : " << (enable_d2 ? "true" : "false") <<  ", "

+ 2 - 2
src/bin/dhcp4/tests/dhcp4_process_tests.sh.in

@@ -32,7 +32,7 @@ CONFIG="{
         \"subnet4\": [
         {
             \"subnet\": \"10.0.0.0/8\",
-            \"pool\": [ \"10.0.0.10-10.0.0.100\" ]
+            \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]
         } ]
     },
 
@@ -68,7 +68,7 @@ CONFIG_INVALID="{
         \"subnet4\": [
         {
             \"subnet\": \"10.0.0.0/8\",
-            \"pool\": [ \"10.0.0.10-10.0.0.100\" ]
+            \"pool\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]
         } ]
     },
 

+ 15 - 15
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc

@@ -1209,7 +1209,7 @@ TEST_F(Dhcpv4SrvTest, vendorOptionsDocsis) {
         "          \"csv-format\": True"
         "        }],"
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"10.254.226.0/25\" ],"
+        "    \"pools\": [ { \"pool\": \"10.254.226.0/25\" } ],"
         "    \"subnet\": \"10.254.226.0/24\", "
         "    \"rebind-timer\": 2000, "
         "    \"renew-timer\": 1000, "
@@ -1457,7 +1457,7 @@ TEST_F(Dhcpv4SrvTest, nextServerOverride) {
         "\"renew-timer\": 1000, "
         "\"next-server\": \"192.0.0.1\", "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\":  \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"next-server\": \"1.2.3.4\", "
         "    \"subnet\": \"192.0.2.0/24\" } ],"
         "\"valid-lifetime\": 4000 }";
@@ -1502,7 +1502,7 @@ TEST_F(Dhcpv4SrvTest, nextServerGlobal) {
         "\"renew-timer\": 1000, "
         "\"next-server\": \"192.0.0.1\", "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\" } ],"
         "\"valid-lifetime\": 4000 }";
 
@@ -2494,11 +2494,11 @@ TEST_F(HooksDhcpv4SrvTest, subnet4SelectSimple) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.0/25\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.0/25\" } ],"
         "    \"subnet\": \"192.0.2.0/24\", "
         "    \"interface\": \"eth0\" "
         " }, {"
-        "    \"pool\": [ \"192.0.3.0/25\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.3.0/25\" } ],"
         "    \"subnet\": \"192.0.3.0/24\" "
         " } ],"
         "\"valid-lifetime\": 4000 }";
@@ -2562,11 +2562,11 @@ TEST_F(HooksDhcpv4SrvTest, subnet4SelectChange) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.0/25\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.0/25\" } ],"
         "    \"subnet\": \"192.0.2.0/24\", "
         "    \"interface\": \"eth0\" "
         " }, {"
-        "    \"pool\": [ \"192.0.3.0/25\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.3.0/25\" } ],"
         "    \"subnet\": \"192.0.3.0/24\" "
         " } ],"
         "\"valid-lifetime\": 4000 }";
@@ -2981,7 +2981,7 @@ TEST_F(Dhcpv4SrvTest, vendorOptionsORO) {
         "          \"csv-format\": True"
         "        }],"
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.0/25\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.0/25\" } ],"
         "    \"subnet\": \"192.0.2.0/24\", "
         "    \"rebind-timer\": 2000, "
         "    \"renew-timer\": 1000, "
@@ -3067,7 +3067,7 @@ TEST_F(Dhcpv4SrvTest, vendorOptionsDocsisDefinitions) {
         "          \"csv-format\": True"
         "        }],"
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.50\" ],"
+        "    \"pools\": [ { \"pool\":  \"192.0.2.1 - 192.0.2.50\" } ],"
         "    \"subnet\": \"192.0.2.0/24\", "
         "    \"renew-timer\": 1000, "
         "    \"rebind-timer\": 1000, "
@@ -3148,10 +3148,10 @@ TEST_F(Dhcpv4SrvTest, clientClassify2) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ "
-        "{   \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "{   \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"client-class\": \"foo\", "
         "    \"subnet\": \"192.0.2.0/24\" }, "
-        "{   \"pool\": [ \"192.0.3.1 - 192.0.3.100\" ],"
+        "{   \"pools\": [ { \"pool\": \"192.0.3.1 - 192.0.3.100\" } ],"
         "    \"client-class\": \"xyzzy\", "
         "    \"subnet\": \"192.0.3.0/24\" } "
         "],"
@@ -3196,12 +3196,12 @@ TEST_F(Dhcpv4SrvTest, relayOverride) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ "
-        "{   \"pool\": [ \"192.0.2.2 - 192.0.2.100\" ],"
+        "{   \"pools\": [ { \"pool\": \"192.0.2.2 - 192.0.2.100\" } ],"
         "    \"relay\": { "
         "        \"ip-address\": \"192.0.5.1\""
         "    },"
         "    \"subnet\": \"192.0.2.0/24\" }, "
-        "{   \"pool\": [ \"192.0.3.1 - 192.0.3.100\" ],"
+        "{   \"pools\": [ { \"pool\": \"192.0.3.1 - 192.0.3.100\" } ],"
         "    \"relay\": { "
         "        \"ip-address\": \"192.0.5.2\""
         "    },"
@@ -3271,13 +3271,13 @@ TEST_F(Dhcpv4SrvTest, relayOverrideAndClientClass) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ "
-        "{   \"pool\": [ \"192.0.2.2 - 192.0.2.100\" ],"
+        "{   \"pools\": [ { \"pool\": \"192.0.2.2 - 192.0.2.100\" } ],"
         "    \"client-class\": \"foo\", "
         "    \"relay\": { "
         "        \"ip-address\": \"192.0.5.1\""
         "    },"
         "    \"subnet\": \"192.0.2.0/24\" }, "
-        "{   \"pool\": [ \"192.0.3.1 - 192.0.3.100\" ],"
+        "{   \"pools\": [ { \"pool\": \"192.0.3.1 - 192.0.3.100\" } ],"
         "    \"relay\": { "
         "        \"ip-address\": \"192.0.5.1\""
         "    },"

+ 3 - 3
src/bin/dhcp4/tests/direct_client_unittest.cc

@@ -120,7 +120,7 @@ DirectClientTest::configureSubnet(const std::string& prefix) {
         "\"renew-timer\": 1000, "
         "\"option-data\": [ ],"
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"" << prefix << "/24\" ],"
+        "    \"pools\": [ { \"pool\": \"" << prefix << "/24\" } ],"
         "    \"subnet\": \"" << prefix << "/24\", "
         "    \"rebind-timer\": 2000, "
         "    \"renew-timer\": 1000, "
@@ -141,14 +141,14 @@ DirectClientTest::configureTwoSubnets(const std::string& prefix1,
         "\"renew-timer\": 1000, "
         "\"option-data\": [ ],"
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"" << prefix1 << "/24\" ],"
+        "    \"pools\": [ { \"pool\": \"" << prefix1 << "/24\" } ],"
         "    \"subnet\": \"" << prefix1 << "/24\", "
         "    \"rebind-timer\": 2000, "
         "    \"renew-timer\": 1000, "
         "    \"valid-lifetime\": 4000"
         " },"
         "{ "
-        "    \"pool\": [ \"" << prefix2 << "/24\" ],"
+        "    \"pools\": [ { \"pool\": \"" << prefix2 << "/24\" } ],"
         "    \"subnet\": \"" << prefix2 << "/24\", "
         "    \"rebind-timer\": 2000, "
         "    \"renew-timer\": 1000, "

+ 1 - 1
src/bin/dhcp4/tests/dora_unittest.cc

@@ -53,7 +53,7 @@ const char* DORA_CONFIGS[] = {
         "\"valid-lifetime\": 600,"
         "\"subnet4\": [ { "
         "    \"subnet\": \"10.0.0.0/24\", "
-        "    \"pool\": [ \"10.0.0.10-10.0.0.100\" ],"
+        "    \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
         "    \"option-data\": [ {"
         "        \"name\": \"routers\","
         "        \"code\": 3,"

+ 1 - 1
src/bin/dhcp4/tests/inform_unittest.cc

@@ -52,7 +52,7 @@ const char* INFORM_CONFIGS[] = {
         "\"valid-lifetime\": 600,"
         "\"subnet4\": [ { "
         "    \"subnet\": \"10.0.0.0/24\", "
-        "    \"pool\": [ \"10.0.0.10-10.0.0.100\" ],"
+        "    \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
         "    \"option-data\": [ {"
         "        \"name\": \"routers\","
         "        \"code\": 3,"

+ 4 - 4
src/bin/dhcp4/tests/kea_controller_unittest.cc

@@ -89,16 +89,16 @@ TEST_F(JSONFileBackendTest, jsonFile) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
         "    \"subnet\": \"192.0.2.0/24\" "
         " },"
         " {"
-        "    \"pool\": [ \"192.0.3.101 - 192.0.3.150\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.3.101 - 192.0.3.150\" } ],"
         "    \"subnet\": \"192.0.3.0/24\", "
         "    \"id\": 0 "
         " },"
         " {"
-        "    \"pool\": [ \"192.0.4.101 - 192.0.4.150\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.4.101 - 192.0.4.150\" } ],"
         "    \"subnet\": \"192.0.4.0/24\" "
         " } ],"
         "\"valid-lifetime\": 4000 }"
@@ -164,7 +164,7 @@ TEST_F(JSONFileBackendTest, comments) {
         "\"renew-timer\": 1000, \n"
         "# comments in the middle should be ignored, too\n"
         "\"subnet4\": [ { "
-        "    \"pool\": [ \"192.0.2.0/24\" ],"
+        "    \"pools\": [ { \"pool\": \"192.0.2.0/24\" } ],"
         "    \"subnet\": \"192.0.2.0/22\" "
         " } ],"
         "\"valid-lifetime\": 4000 }"

+ 15 - 3
src/bin/dhcp6/json_config_parser.cc

@@ -122,7 +122,7 @@ protected:
     }
 };
 
-/// @brief Parser for IPv4 pool definitions.
+/// @brief Parser for IPv6 pool definitions.
 ///
 /// This is the IPv6 derivation of the PoolParser class and handles pool
 /// definitions, i.e. a list of entries of one of two syntaxes: min-max and
@@ -173,6 +173,18 @@ protected:
     }
 };
 
+class Pools6ListParser : public PoolsListParser {
+public:
+    Pools6ListParser(const std::string& dummy, PoolStoragePtr pools)
+        :PoolsListParser(dummy, pools) {
+    }
+
+protected:
+    virtual ParserPtr poolParserMaker(PoolStoragePtr storage) {
+        return (ParserPtr(new Pool6Parser("pool", storage)));
+    }
+};
+
 /// @brief Parser for IPv6 prefix delegation definitions.
 ///
 /// This class handles prefix delegation pool definitions for IPv6 subnets
@@ -436,8 +448,8 @@ protected:
                    (config_id.compare("client-class") == 0) ||
                    (config_id.compare("interface-id") == 0)) {
             parser = new StringParser(config_id, string_values_);
-        } else if (config_id.compare("pool") == 0) {
-            parser = new Pool6Parser(config_id, pools_);
+        } else if (config_id.compare("pools") == 0) {
+            parser = new Pools6ListParser(config_id, pools_);
         } else if (config_id.compare("relay") == 0) {
             parser = new RelayInfoParser(config_id, relay_info_, Option::V6);
         } else if (config_id.compare("pd-pools") == 0) {

+ 106 - 49
src/bin/dhcp6/tests/config_parser_unittest.cc

@@ -195,7 +195,7 @@ public:
             "  \"encapsulate\": \"\""
             "} ],"
             "\"subnet6\": [ { "
-            "    \"pool\": [ \"2001:db8:1::/80\" ],"
+            "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
             "    \"subnet\": \"2001:db8:1::/64\", "
             "    \"option-data\": [ {";
         bool first = true;
@@ -537,7 +537,7 @@ TEST_F(Dhcp6ParserTest, subnetGlobalDefaults) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::1 - 2001:db8:1::ffff\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ],"
         "    \"subnet\": \"2001:db8:1::/64\" } ],"
         "\"valid-lifetime\": 4000 }";
 
@@ -562,6 +562,7 @@ TEST_F(Dhcp6ParserTest, subnetGlobalDefaults) {
     EXPECT_EQ(1, subnet->getID());
 }
 
+// This test checks that multiple subnets can be defined and handled properly.
 TEST_F(Dhcp6ParserTest, multipleSubnets) {
     ConstElementPtr x;
     // Collection of four subnets for which ids should be autogenerated
@@ -571,20 +572,20 @@ TEST_F(Dhcp6ParserTest, multipleSubnets) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
         "    \"subnet\": \"2001:db8:1::/64\" "
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:2::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:2::/80\" } ],"
         "    \"subnet\": \"2001:db8:2::/64\", "
         "    \"id\": 0"
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:3::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:3::/80\" } ],"
         "    \"subnet\": \"2001:db8:3::/64\" "
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:4::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:4::/80\" } ],"
         "    \"subnet\": \"2001:db8:4::/64\" "
         " } ],"
         "\"valid-lifetime\": 4000 }";
@@ -623,22 +624,22 @@ TEST_F(Dhcp6ParserTest, multipleSubnetsExplicitIDs) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
         "    \"subnet\": \"2001:db8:1::/64\", "
         "    \"id\": 1024"
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:2::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:2::/80\" } ],"
         "    \"subnet\": \"2001:db8:2::/64\", "
         "    \"id\": 100"
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:3::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:3::/80\" } ],"
         "    \"subnet\": \"2001:db8:3::/64\", "
         "    \"id\": 1"
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:4::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:4::/80\" } ],"
         "    \"subnet\": \"2001:db8:4::/64\", "
         "    \"id\": 34"
         " } ],"
@@ -676,22 +677,22 @@ TEST_F(Dhcp6ParserTest, multipleSubnetsOverlapingIDs) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
         "    \"subnet\": \"2001:db8:1::/64\", "
         "    \"id\": 1024"
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:2::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:2::/80\" } ],"
         "    \"subnet\": \"2001:db8:2::/64\", "
         "    \"id\": 100"
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:3::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:3::/80\" } ],"
         "    \"subnet\": \"2001:db8:3::/64\", "
         "    \"id\": 1024"
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:4::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:4::/80\" } ],"
         "    \"subnet\": \"2001:db8:4::/64\", "
         "    \"id\": 34"
         " } ],"
@@ -716,22 +717,22 @@ TEST_F(Dhcp6ParserTest, reconfigureRemoveSubnet) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
         "    \"subnet\": \"2001:db8:1::/64\", "
         "    \"id\": 1"
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:2::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:2::/80\" } ],"
         "    \"subnet\": \"2001:db8:2::/64\", "
         "    \"id\": 2"
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:3::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:3::/80\" } ],"
         "    \"subnet\": \"2001:db8:3::/64\", "
         "    \"id\": 3"
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:4::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:4::/80\" } ],"
         "    \"subnet\": \"2001:db8:4::/64\", "
         "    \"id\": 4"
         " } ],"
@@ -743,17 +744,17 @@ TEST_F(Dhcp6ParserTest, reconfigureRemoveSubnet) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
         "    \"subnet\": \"2001:db8:1::/64\", "
         "    \"id\": 1"
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:2::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:2::/80\" } ],"
         "    \"subnet\": \"2001:db8:2::/64\", "
         "    \"id\": 2"
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:3::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:3::/80\" } ],"
         "    \"subnet\": \"2001:db8:3::/64\", "
         "    \"id\": 3"
         " } ],"
@@ -765,17 +766,17 @@ TEST_F(Dhcp6ParserTest, reconfigureRemoveSubnet) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
         "    \"subnet\": \"2001:db8:1::/64\", "
         "    \"id\": 1"
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:3::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:3::/80\" } ],"
         "    \"subnet\": \"2001:db8:3::/64\", "
         "    \"id\": 3"
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:4::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:4::/80\" } ],"
         "    \"subnet\": \"2001:db8:4::/64\", "
         "    \"id\": 4"
         " } ],"
@@ -840,7 +841,7 @@ TEST_F(Dhcp6ParserTest, subnetLocal) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::1 - 2001:db8:1::ffff\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ],"
         "    \"renew-timer\": 1, "
         "    \"rebind-timer\": 2, "
         "    \"preferred-lifetime\": 3,"
@@ -877,7 +878,7 @@ TEST_F(Dhcp6ParserTest, subnetInterface) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::1 - 2001:db8:1::ffff\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ],"
         "    \"interface\": \"" + valid_iface_ + "\","
         "    \"subnet\": \"2001:db8:1::/64\" } ],"
         "\"valid-lifetime\": 4000 }";
@@ -909,7 +910,7 @@ TEST_F(Dhcp6ParserTest, subnetInterfaceBogus) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::1 - 2001:db8:1::ffff\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ],"
         "    \"interface\": \"" + bogus_iface_ + "\","
         "    \"subnet\": \"2001:db8:1::/64\" } ],"
         "\"valid-lifetime\": 4000 }";
@@ -941,7 +942,7 @@ TEST_F(Dhcp6ParserTest, interfaceGlobal) {
         "\"renew-timer\": 1000, "
         "\"interface\": \"" + valid_iface_ + "\"," // Not valid. Can be defined in subnet only
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::1 - 2001:db8:1::ffff\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ],"
         "    \"subnet\": \"2001:db8:1::/64\" } ],"
         "\"valid-lifetime\": 4000 }";
     cout << config << endl;
@@ -970,7 +971,7 @@ TEST_F(Dhcp6ParserTest, subnetInterfaceId) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::1 - 2001:db8:1::ffff\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ],"
         "    \"interface-id\": \"" + valid_interface_id + "\","
         "    \"subnet\": \"2001:db8:1::/64\" } ],"
         "\"valid-lifetime\": 4000 }";
@@ -1008,7 +1009,7 @@ TEST_F(Dhcp6ParserTest, interfaceIdGlobal) {
         "\"renew-timer\": 1000, "
         "\"interface-id\": \"foobar\"," // Not valid. Can be defined in subnet only
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::1 - 2001:db8:1::ffff\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ],"
         "    \"subnet\": \"2001:db8:1::/64\" } ],"
         "\"valid-lifetime\": 4000 }";
 
@@ -1030,7 +1031,7 @@ TEST_F(Dhcp6ParserTest, subnetInterfaceAndInterfaceId) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::1 - 2001:db8:1::ffff\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ],"
         "    \"interface\": \"" + valid_iface_ + "\","
         "    \"interface-id\": \"foobar\","
         "    \"subnet\": \"2001:db8:1::/64\" } ],"
@@ -1046,6 +1047,62 @@ TEST_F(Dhcp6ParserTest, subnetInterfaceAndInterfaceId) {
     EXPECT_TRUE(errorContainsPosition(status, "<string>"));
 }
 
+// This test checks that multiple pools can be defined and handled properly.
+// The test defines 2 subnets, each with 2 pools.
+TEST_F(Dhcp6ParserTest, multiplePools) {
+    // Collection with two subnets, each with 2 pools.
+    string config = "{ \"interfaces\": [ \"*\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"pools\": [ "
+        "        { \"pool\": \"2001:db8:1::/96\" },"
+        "        { \"pool\": \"2001:db8:1:0:abcd::/112\" }"
+        "    ],"
+        "    \"subnet\": \"2001:db8:1::/64\" "
+        " },"
+        " {"
+        "    \"pools\": [ "
+        "    { \"pool\": \"2001:db8:2::1 - 2001:db8:2::ff\" },"
+        "    { \"pool\": \"2001:db8:2::300 - 2001:db8:2::3ff\" }"
+        "    ],"
+        "    \"subnet\": \"2001:db8:2::/64\""
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+    ElementPtr json;
+    ASSERT_NO_THROW(json = Element::fromJSON(config));
+
+    ConstElementPtr status;
+    ASSERT_NO_THROW(status = configureDhcp6Server(srv_, json));
+    checkResult(status, 0);
+
+    const Subnet6Collection* subnets = CfgMgr::instance().getSubnets6();
+    ASSERT_TRUE(subnets);
+    ASSERT_EQ(2, subnets->size()); // We expect 2 subnets
+
+    // Check the first subnet
+    const PoolCollection& pools1 = subnets->at(0)->getPools(Lease::TYPE_NA);
+    ASSERT_EQ(2, pools1.size());
+    EXPECT_EQ("type=IA_NA, 2001:db8:1::-2001:db8:1::ffff:ffff, delegated_len=128",
+              pools1[0]->toText());
+    EXPECT_EQ("type=IA_NA, 2001:db8:1:0:abcd::-2001:db8:1:0:abcd::ffff, delegated_len=128",
+              pools1[1]->toText());
+    // There shouldn't be any TA or PD pools
+    EXPECT_TRUE(subnets->at(0)->getPools(Lease::TYPE_TA).empty());
+    EXPECT_TRUE(subnets->at(0)->getPools(Lease::TYPE_PD).empty());
+
+    // Check the second subnet
+    const PoolCollection& pools2 = subnets->at(1)->getPools(Lease::TYPE_NA);
+    ASSERT_EQ(2, pools2.size());
+    EXPECT_EQ("type=IA_NA, 2001:db8:2::1-2001:db8:2::ff, delegated_len=128",
+              pools2[0]->toText());
+    EXPECT_EQ("type=IA_NA, 2001:db8:2::300-2001:db8:2::3ff, delegated_len=128",
+              pools2[1]->toText());
+    // There shouldn't be any TA or PD pools
+    EXPECT_TRUE(subnets->at(0)->getPools(Lease::TYPE_TA).empty());
+    EXPECT_TRUE(subnets->at(0)->getPools(Lease::TYPE_PD).empty());
+}
 
 
 // Test verifies that a subnet with pool values that do not belong to that
@@ -1059,7 +1116,7 @@ TEST_F(Dhcp6ParserTest, poolOutOfSubnet) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"4001:db8:1::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"4001:db8:1::/80\" } ],"
         "    \"subnet\": \"2001:db8:1::/64\" } ],"
         "\"valid-lifetime\": 4000 }";
 
@@ -1088,7 +1145,7 @@ TEST_F(Dhcp6ParserTest, poolPrefixLen) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
         "    \"subnet\": \"2001:db8:1::/64\" } ],"
         "\"valid-lifetime\": 4000 }";
 
@@ -1183,7 +1240,7 @@ TEST_F(Dhcp6ParserTest, pdPoolList) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1:04::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1:04::/80\" } ],"
         "    \"subnet\": \"2001:db8:1::/40\","
         "    \"pd-pools\": ["
         "        { \"prefix\": \"2001:db8:1:01::\", "
@@ -1941,7 +1998,7 @@ TEST_F(Dhcp6ParserTest, optionDataDefaults) {
         "    \"csv-format\": True"
         " } ],"
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
         "    \"subnet\": \"2001:db8:1::/64\""
         " } ],"
         "\"valid-lifetime\": 4000 }";
@@ -2032,7 +2089,7 @@ TEST_F(Dhcp6ParserTest, optionDataTwoSpaces) {
         "    \"encapsulate\": \"\""
         " } ],"
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
         "    \"subnet\": \"2001:db8:1::/64\""
         " } ]"
         "}";
@@ -2187,7 +2244,7 @@ TEST_F(Dhcp6ParserTest, optionDataEncapsulate) {
         "    \"encapsulate\": \"\""
         " } ],"
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
         "    \"subnet\": \"2001:db8:1::/64\""
         " } ]"
         "}";
@@ -2235,7 +2292,7 @@ TEST_F(Dhcp6ParserTest, optionDataInMultipleSubnets) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
         "    \"subnet\": \"2001:db8:1::/64\", "
         "    \"option-data\": [ {"
         "          \"name\": \"subscriber-id\","
@@ -2246,7 +2303,7 @@ TEST_F(Dhcp6ParserTest, optionDataInMultipleSubnets) {
         "        } ]"
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:2::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:2::/80\" } ],"
         "    \"subnet\": \"2001:db8:2::/64\", "
         "    \"option-data\": [ {"
         "          \"name\": \"user-class\","
@@ -2565,7 +2622,7 @@ TEST_F(Dhcp6ParserTest, vendorOptionsHex) {
         "    \"csv-format\": False"
         " } ],"
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
         "    \"subnet\": \"2001:db8:1::/64\""
         " } ]"
         "}";
@@ -2627,7 +2684,7 @@ TEST_F(Dhcp6ParserTest, vendorOptionsCsv) {
         "    \"encapsulate\": \"\""
         " } ],"
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
         "    \"subnet\": \"2001:db8:1::/64\""
         " } ]"
         "}";
@@ -2765,7 +2822,7 @@ TEST_F(Dhcp6ParserTest, stdOptionDataEncapsulate) {
         "    \"encapsulate\": \"\""
         " } ],"
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
         "    \"subnet\": \"2001:db8:1::/64\""
         " } ]"
         "}";
@@ -3049,7 +3106,7 @@ TEST_F(Dhcp6ParserTest, subnetRelayInfo) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::1 - 2001:db8:1::ffff\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ],"
         "    \"relay\": { "
         "        \"ip-address\": \"2001:db8:1::abcd\""
         "    },"
@@ -3079,22 +3136,22 @@ TEST_F(Dhcp6ParserTest, classifySubnets) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
         "    \"subnet\": \"2001:db8:1::/64\", "
         "    \"client-class\": \"alpha\" "
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:2::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:2::/80\" } ],"
         "    \"subnet\": \"2001:db8:2::/64\", "
         "    \"client-class\": \"beta\" "
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:3::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:3::/80\" } ],"
         "    \"subnet\": \"2001:db8:3::/64\", "
         "    \"client-class\": \"gamma\" "
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:4::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:4::/80\" } ],"
         "    \"subnet\": \"2001:db8:4::/64\" "
         " } ],"
         "\"valid-lifetime\": 4000 }";
@@ -3174,7 +3231,7 @@ TEST_F(Dhcp6ParserTest, d2ClientConfig) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
         "    \"subnet\": \"2001:db8:1::/64\" } ], "
         " \"dhcp-ddns\" : {"
         "     \"enable-updates\" : true, "
@@ -3239,7 +3296,7 @@ TEST_F(Dhcp6ParserTest, invalidD2ClientConfig) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
         "    \"subnet\": \"2001:db8:1::/64\" } ], "
         " \"dhcp-ddns\" : {"
         "     \"enable-updates\" : true, "

+ 4 - 4
src/bin/dhcp6/tests/confirm_unittest.cc

@@ -47,13 +47,13 @@ const char* CONFIRM_CONFIGS[] = {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/64\" ],"
+        "    \"pools\": [  { \"pool\": \"2001:db8:1::/64\" } ],"
         "    \"subnet\": \"2001:db8:1::/48\", "
         "    \"interface-id\": \"\","
         "    \"interface\": \"eth0\""
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:2::/64\" ],"
+        "    \"pools\": [  { \"pool\": \"2001:db8:2::/64\" } ],"
         "    \"subnet\": \"2001:db8:2::/48\", "
         "    \"interface-id\": \"\","
         "    \"interface\": \"eth1\""
@@ -66,13 +66,13 @@ const char* CONFIRM_CONFIGS[] = {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"3000:1::/64\" ],"
+        "    \"pools\": [  { \"pool\": \"3000:1::/64\" } ],"
         "    \"subnet\": \"3000:1::/48\", "
         "    \"interface-id\": \"\","
         "    \"interface\": \"eth0\""
         " },"
         " {"
-        "    \"pool\": [ \"3000:2::/64\" ],"
+        "    \"pools\": [  { \"pool\": \"3000:2::/64\" } ],"
         "    \"subnet\": \"3000:2::/48\", "
         "    \"interface-id\": \"\","
         "    \"interface\": \"eth1\""

+ 3 - 3
src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc

@@ -165,16 +165,16 @@ TEST_F(CtrlDhcpv6SrvTest, configReload) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
         "    \"subnet\": \"2001:db8:1::/64\" "
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:2::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:2::/80\" } ],"
         "    \"subnet\": \"2001:db8:2::/64\", "
         "    \"id\": 0"
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:3::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:3::/80\" } ],"
         "    \"subnet\": \"2001:db8:3::/64\" "
         " } ],"
         "\"valid-lifetime\": 4000 }";

+ 1 - 1
src/bin/dhcp6/tests/d2_unittest.cc

@@ -103,7 +103,7 @@ Dhcp6SrvD2Test::configureD2(bool enable_d2, const bool exp_result,
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::1 - 2001:db8:1::ffff\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ],"
         "    \"subnet\": \"2001:db8:1::/64\" } ],"
         " \"dhcp-ddns\" : {"
         "     \"enable-updates\" : " << (enable_d2 ? "true" : "false") <<  ", "

+ 2 - 2
src/bin/dhcp6/tests/dhcp6_process_tests.sh.in

@@ -33,7 +33,7 @@ CONFIG="{
         \"subnet6\": [
         {
             \"subnet\": \"2001:db8:1::/64\",
-            \"pool\": [ \"2001:db8:1::10-2001:db8:1::100\" ]
+            \"pools\": [ { \"pool\": \"2001:db8:1::10-2001:db8:1::100\" } ]
         } ]
     },
 
@@ -70,7 +70,7 @@ CONFIG_INVALID="{
         \"subnet6\": [
         {
             \"subnet\": \"2001:db8:1::/64\",
-            \"pool\": [ \"2001:db8:1::10-2001:db8:1::100\" ]
+            \"pool\": [ { \"pool\": \"2001:db8:1::10-2001:db8:1::100\" } ]
         } ]
     },
 

+ 9 - 9
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc

@@ -294,7 +294,7 @@ TEST_F(Dhcpv6SrvTest, advertiseOptions) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/64\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
         "    \"subnet\": \"2001:db8:1::/48\", "
         "    \"interface\": \"eth0\", "
         "    \"option-data\": [ {"
@@ -1578,7 +1578,7 @@ TEST_F(Dhcpv6SrvTest, vendorOptionsORO) {
         "          \"csv-format\": True"
         "        }],"
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/64\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
         "    \"subnet\": \"2001:db8:1::/48\", "
         "    \"renew-timer\": 1000, "
         "    \"rebind-timer\": 1000, "
@@ -1655,7 +1655,7 @@ TEST_F(Dhcpv6SrvTest, vendorOptionsDocsisDefinitions) {
         "          \"csv-format\": True"
         "        }],"
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/64\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
         "    \"subnet\": \"2001:db8:1::/48\", "
         "    \"renew-timer\": 1000, "
         "    \"rebind-timer\": 1000, "
@@ -1811,11 +1811,11 @@ TEST_F(Dhcpv6SrvTest, clientClassify2) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ "
-        " {  \"pool\": [ \"2001:db8:1::/64\" ],"
+        " {  \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
         "    \"subnet\": \"2001:db8:1::/48\", "
         "    \"client-class\": \"foo\" "
         " }, "
-        " {  \"pool\": [ \"2001:db8:2::/64\" ],"
+        " {  \"pools\": [ { \"pool\": \"2001:db8:2::/64\" } ],"
         "    \"subnet\": \"2001:db8:2::/48\", "
         "    \"client-class\": \"xyzzy\" "
         " } "
@@ -1885,13 +1885,13 @@ TEST_F(Dhcpv6SrvTest, relayOverride) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ "
-        " {  \"pool\": [ \"2001:db8:1::/64\" ],"
+        " {  \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
         "    \"subnet\": \"2001:db8:1::/48\", "
         "    \"relay\": { "
         "        \"ip-address\": \"2001:db8:3::1\""
         "    }"
         " }, "
-        " {  \"pool\": [ \"2001:db8:2::/64\" ],"
+        " {  \"pools\": [ { \"pool\": \"2001:db8:2::/64\" } ],"
         "    \"subnet\": \"2001:db8:2::/48\", "
         "    \"relay\": { "
         "        \"ip-address\": \"2001:db8:3::2\""
@@ -1960,14 +1960,14 @@ TEST_F(Dhcpv6SrvTest, relayOverrideAndClientClass) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ "
-        " {  \"pool\": [ \"2001:db8:1::/64\" ],"
+        " {  \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
         "    \"subnet\": \"2001:db8:1::/48\", "
         "    \"client-class\": \"foo\", "
         "    \"relay\": { "
         "        \"ip-address\": \"2001:db8:3::1\""
         "    }"
         " }, "
-        " {  \"pool\": [ \"2001:db8:2::/64\" ],"
+        " {  \"pools\": [ { \"pool\": \"2001:db8:2::/64\" } ],"
         "    \"subnet\": \"2001:db8:2::/48\", "
         "    \"relay\": { "
         "        \"ip-address\": \"2001:db8:3::1\""

+ 4 - 4
src/bin/dhcp6/tests/hooks_unittest.cc

@@ -904,11 +904,11 @@ TEST_F(HooksDhcpv6SrvTest, subnet6_select) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/64\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
         "    \"subnet\": \"2001:db8:1::/48\", "
         "    \"interface\": \"" + valid_iface_ + "\" "
         " }, {"
-        "    \"pool\": [ \"2001:db8:2::/64\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:2::/64\" } ],"
         "    \"subnet\": \"2001:db8:2::/48\" "
         " } ],"
         "\"valid-lifetime\": 4000 }";
@@ -972,11 +972,11 @@ TEST_F(HooksDhcpv6SrvTest, subnet_select_change) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/64\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
         "    \"subnet\": \"2001:db8:1::/48\", "
         "    \"interface\": \"" + valid_iface_ + "\" "
         " }, {"
-        "    \"pool\": [ \"2001:db8:2::/64\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:2::/64\" } ],"
         "    \"subnet\": \"2001:db8:2::/48\" "
         " } ],"
         "\"valid-lifetime\": 4000 }";

+ 4 - 4
src/bin/dhcp6/tests/kea_controller_unittest.cc

@@ -78,16 +78,16 @@ TEST_F(JSONFileBackendTest, jsonFile) {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
         "    \"subnet\": \"2001:db8:1::/64\" "
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:2::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:2::/80\" } ],"
         "    \"subnet\": \"2001:db8:2::/64\", "
         "    \"id\": 0"
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:3::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:3::/80\" } ],"
         "    \"subnet\": \"2001:db8:3::/64\" "
         " } ],"
         "\"valid-lifetime\": 4000 }"
@@ -153,7 +153,7 @@ TEST_F(JSONFileBackendTest, comments) {
         "\"renew-timer\": 1000, \n"
         "# comments in the middle should be ignored, too\n"
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
         "    \"subnet\": \"2001:db8:1::/64\" "
         " } ],"
         "\"valid-lifetime\": 4000 }"

+ 8 - 8
src/bin/dhcp6/tests/rebind_unittest.cc

@@ -69,13 +69,13 @@ const char* REBIND_CONFIGS[] = {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:1::/64\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
         "    \"subnet\": \"2001:db8:1::/48\", "
         "    \"interface-id\": \"\","
         "    \"interface\": \"eth0\""
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:2::/64\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:2::/64\" } ],"
         "    \"subnet\": \"2001:db8:2::/48\", "
         "    \"interface-id\": \"\","
         "    \"interface\": \"eth1\""
@@ -88,13 +88,13 @@ const char* REBIND_CONFIGS[] = {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"2001:db8:3::/64\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:3::/64\" } ],"
         "    \"subnet\": \"2001:db8:3::/48\", "
         "    \"interface-id\": \"\","
         "    \"interface\": \"eth1\""
         " },"
         " {"
-        "    \"pool\": [ \"2001:db8:4::/64\" ],"
+        "    \"pools\": [ { \"pool\": \"2001:db8:4::/64\" } ],"
         "    \"subnet\": \"2001:db8:4::/48\", "
         "    \"interface-id\": \"\","
         "    \"interface\": \"eth0\""
@@ -107,13 +107,13 @@ const char* REBIND_CONFIGS[] = {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"3000:1::/64\" ],"
+        "    \"pools\": [ { \"pool\": \"3000:1::/64\" } ],"
         "    \"subnet\": \"3000:1::/48\", "
         "    \"interface-id\": \"\","
         "    \"interface\": \"eth0\""
         " },"
         " {"
-        "    \"pool\": [ \"3000:2::/64\" ],"
+        "    \"pools\": [ { \"pool\": \"3000:2::/64\" } ],"
         "    \"subnet\": \"3000:2::/48\", "
         "    \"interface-id\": \"\","
         "    \"interface\": \"eth1\""
@@ -126,13 +126,13 @@ const char* REBIND_CONFIGS[] = {
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
-        "    \"pool\": [ \"3000:3::/64\" ],"
+        "    \"pools\": [ { \"pool\": \"3000:3::/64\" } ],"
         "    \"subnet\": \"3000:3::/48\", "
         "    \"interface-id\": \"\","
         "    \"interface\": \"eth1\""
         " },"
         " {"
-        "    \"pool\": [ \"3000:4::/64\" ],"
+        "    \"pools\": [ { \"pool\": \"3000:4::/64\" } ],"
         "    \"subnet\": \"3000:4::/48\", "
         "    \"interface-id\": \"\","
         "    \"interface\": \"eth0\""

+ 2 - 2
src/bin/keactrl/tests/keactrl_tests.sh.in

@@ -47,7 +47,7 @@ config="{
         \"subnet4\": [
         {
             \"subnet\": \"10.0.0.0/24\",
-            \"pool\": [ \"10.0.0.10-10.0.0.100\" ]
+            \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]
         } ]
     },
     \"Dhcp6\":
@@ -65,7 +65,7 @@ config="{
         \"subnet6\": [
         {
             \"subnet\": \"2001:db8:1::/64\",
-            \"pool\": [ \"2001:db8:1::10-2001:db8:1::100\" ]
+            \"pools\": [ { \"pool\": \"2001:db8:1::10-2001:db8:1::100\" } ]
         } ]
     },
     \"DhcpDdns\":

+ 111 - 65
src/lib/dhcpsrv/dhcp_parsers.cc

@@ -335,12 +335,12 @@ OptionDataParser::OptionDataParser(const std::string&, OptionStoragePtr options,
     options_(options), option_descriptor_(false),
     global_context_(global_context) {
     if (!options_) {
-        isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+        isc_throw(isc::dhcp::DhcpConfigError, "parser logic error: "
              << "options storage may not be NULL");
     }
 
     if (!global_context_) {
-        isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+        isc_throw(isc::dhcp::DhcpConfigError, "parser logic error: "
              << "context may may not be NULL");
     }
 }
@@ -612,17 +612,17 @@ OptionDataListParser::OptionDataListParser(const std::string&,
     global_context_(global_context),
     optionDataParserFactory_(optionDataParserFactory) {
     if (!options_) {
-        isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+        isc_throw(isc::dhcp::DhcpConfigError, "parser logic error: "
              << "options storage may not be NULL");
     }
 
     if (!options_) {
-        isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+        isc_throw(isc::dhcp::DhcpConfigError, "parser logic error: "
              << "context may not be NULL");
     }
 
     if (!optionDataParserFactory_) {
-        isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+        isc_throw(isc::dhcp::DhcpConfigError, "parser logic error: "
              << "option data parser factory may not be NULL");
     }
 }
@@ -665,7 +665,7 @@ OptionDefParser::OptionDefParser(const std::string&,
       uint32_values_(new Uint32Storage()),
       global_context_(global_context) {
     if (!storage_) {
-        isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+        isc_throw(isc::dhcp::DhcpConfigError, "parser logic error: "
              << "options storage may not be NULL");
     }
 }
@@ -823,7 +823,7 @@ OptionDefListParser::OptionDefListParser(const std::string&,
     : storage_(global_context->option_defs_),
       global_context_(global_context) {
     if (!storage_) {
-        isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+        isc_throw(isc::dhcp::DhcpConfigError, "parser logic error: "
              << "storage may not be NULL");
     }
 }
@@ -888,7 +888,7 @@ RelayInfoParser::RelayInfoParser(const std::string&,
                                   family == Option::V4 ? "0.0.0.0" : "::")),
      string_values_(new StringStorage()), family_(family) {
     if (!relay_info) {
-        isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+        isc_throw(isc::dhcp::DhcpConfigError, "parser logic error: "
                   << "relay-info storage may not be NULL");
     }
 
@@ -943,77 +943,123 @@ RelayInfoParser::commit() {
     *storage_ = local_;
 }
 
+//****************************** PoolsListParser ********************************
+PoolsListParser::PoolsListParser(const std::string&, PoolStoragePtr pools)
+    :pools_(pools), local_pools_(new PoolStorage()) {
+    if (!pools_) {
+        isc_throw(isc::dhcp::DhcpConfigError, "parser logic error: "
+                  << "storage may not be NULL");
+    }
+}
+
+void
+PoolsListParser::build(ConstElementPtr pools) {
+    BOOST_FOREACH(ConstElementPtr pool, pools->listValue()) {
+
+        // Iterate over every structure on the pools list and invoke
+        // a separate parser for it.
+        ParserPtr parser = poolParserMaker(local_pools_);
+
+        parser->build(pool);
+
+        // Let's store the parser, but do not commit anything yet
+        parsers_.push_back(parser);
+    }
+}
+
+void PoolsListParser::commit() {
+
+    // Commit each parser first. It will store the pool structure
+    // in pools_.
+    BOOST_FOREACH(ParserPtr parser, parsers_) {
+        parser->commit();
+    }
+
+    if (pools_) {
+        // local_pools_ holds the values produced by the build function.
+        // At this point parsing should have completed successfuly so
+        // we can append new data to the supplied storage.
+        pools_->insert(pools_->end(), local_pools_->begin(), local_pools_->end());
+    }
+}
+
 //****************************** PoolParser ********************************
 PoolParser::PoolParser(const std::string&,  PoolStoragePtr pools)
         :pools_(pools) {
 
     if (!pools_) {
-        isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+        isc_throw(isc::dhcp::DhcpConfigError, "parser logic error: "
                   << "storage may not be NULL");
     }
 }
 
 void
-PoolParser::build(ConstElementPtr pools_list) {
-    BOOST_FOREACH(ConstElementPtr text_pool, pools_list->listValue()) {
-        // That should be a single pool representation. It should contain
-        // text is form prefix/len or first - last. Note that spaces
-        // are allowed
-        string txt = text_pool->stringValue();
-
-        // first let's remove any whitespaces
-        boost::erase_all(txt, " "); // space
-        boost::erase_all(txt, "\t"); // tabulation
-
-        // Is this prefix/len notation?
-        size_t pos = txt.find("/");
-        if (pos != string::npos) {
-            isc::asiolink::IOAddress addr("::");
-            uint8_t len = 0;
-            try {
-                addr = isc::asiolink::IOAddress(txt.substr(0, pos));
-
-                // start with the first character after /
-                string prefix_len = txt.substr(pos + 1);
-
-                // It is lexical cast to int and then downcast to uint8_t.
-                // Direct cast to uint8_t (which is really an unsigned char)
-                // will result in interpreting the first digit as output
-                // value and throwing exception if length is written on two
-                // digits (because there are extra characters left over).
-
-                // No checks for values over 128. Range correctness will
-                // be checked in Pool4 constructor.
-                len = boost::lexical_cast<int>(prefix_len);
-            } catch (...)  {
-                isc_throw(DhcpConfigError, "Failed to parse pool "
-                          "definition: " << text_pool->stringValue()
-                          << " (" << text_pool->getPosition() << ")");
-            }
+PoolParser::build(ConstElementPtr pool_structure) {
 
-            PoolPtr pool(poolMaker(addr, len));
-            local_pools_.push_back(pool);
-            continue;
-        }
+    ConstElementPtr text_pool = pool_structure->get("pool");
 
-        // Is this min-max notation?
-        pos = txt.find("-");
-        if (pos != string::npos) {
-            // using min-max notation
-            isc::asiolink::IOAddress min(txt.substr(0,pos));
-            isc::asiolink::IOAddress max(txt.substr(pos + 1));
+    if (!text_pool) {
+        isc_throw(DhcpConfigError, "Mandatory 'pool' entry missing in "
+                  "definition: (" << text_pool->getPosition() << ")");
+    }
 
-            PoolPtr pool(poolMaker(min, max));
-            local_pools_.push_back(pool);
-            continue;
-        }
+    // That should be a single pool representation. It should contain
+    // text is form prefix/len or first - last. Note that spaces
+    // are allowed
+    string txt = text_pool->stringValue();
 
-        isc_throw(DhcpConfigError, "invalid pool definition: "
-                  << text_pool->stringValue() <<
-                  ". There are two acceptable formats <min address-max address>"
-                  " or <prefix/len> ("
-                  << text_pool->getPosition() << ")");
+    // first let's remove any whitespaces
+    boost::erase_all(txt, " "); // space
+    boost::erase_all(txt, "\t"); // tabulation
+
+    // Is this prefix/len notation?
+    size_t pos = txt.find("/");
+    if (pos != string::npos) {
+        isc::asiolink::IOAddress addr("::");
+        uint8_t len = 0;
+        try {
+            addr = isc::asiolink::IOAddress(txt.substr(0, pos));
+
+            // start with the first character after /
+            string prefix_len = txt.substr(pos + 1);
+
+            // It is lexical cast to int and then downcast to uint8_t.
+            // Direct cast to uint8_t (which is really an unsigned char)
+            // will result in interpreting the first digit as output
+            // value and throwing exception if length is written on two
+            // digits (because there are extra characters left over).
+
+            // No checks for values over 128. Range correctness will
+            // be checked in Pool4 constructor.
+            len = boost::lexical_cast<int>(prefix_len);
+        } catch (...)  {
+            isc_throw(DhcpConfigError, "Failed to parse pool "
+                      "definition: " << text_pool->stringValue()
+                      << " (" << text_pool->getPosition() << ")");
         }
+
+        PoolPtr pool(poolMaker(addr, len));
+        local_pools_.push_back(pool);
+        return;
+    }
+
+    // Is this min-max notation?
+    pos = txt.find("-");
+    if (pos != string::npos) {
+        // using min-max notation
+        isc::asiolink::IOAddress min(txt.substr(0,pos));
+        isc::asiolink::IOAddress max(txt.substr(pos + 1));
+
+        PoolPtr pool(poolMaker(min, max));
+        local_pools_.push_back(pool);
+        return;
+    }
+
+    isc_throw(DhcpConfigError, "invalid pool definition: "
+              << text_pool->stringValue() <<
+              ". There are two acceptable formats <min address-max address>"
+              " or <prefix/len> ("
+              << text_pool->getPosition() << ")");
 }
 
 void
@@ -1038,7 +1084,7 @@ SubnetConfigParser::SubnetConfigParser(const std::string&,
     // The first parameter should always be "subnet", but we don't check
     // against that here in case some wants to reuse this parser somewhere.
     if (!global_context_) {
-        isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+        isc_throw(isc::dhcp::DhcpConfigError, "parser logic error: "
                  << "context storage may not be NULL");
     }
 }

+ 60 - 5
src/lib/dhcpsrv/dhcp_parsers.h

@@ -790,13 +790,13 @@ public:
 typedef std::vector<PoolPtr> PoolStorage;
 typedef boost::shared_ptr<PoolStorage> PoolStoragePtr;
 
-/// @brief parser for pool definition
+/// @brief parser for a single pool definition
 ///
 /// This abstract parser handles pool definitions, i.e. a list of entries of one
 /// of two syntaxes: min-max and prefix/len. Pool objects are created
 /// and stored in chosen PoolStorage container.
 ///
-/// It is useful for parsing Dhcp<4/6>/subnet<4/6>[X]/pool parameters.
+/// It is useful for parsing Dhcp<4/6>/subnet<4/6>[X]/pools[X] structure.
 class PoolParser : public DhcpConfigParser {
 public:
 
@@ -809,14 +809,14 @@ public:
     /// @throw isc::dhcp::DhcpConfigError if storage is null.
     PoolParser(const std::string& dummy, PoolStoragePtr pools);
 
-    /// @brief parses the actual list
+    /// @brief parses the actual structure
     ///
     /// This method parses the actual list of interfaces.
     /// No validation is done at this stage, everything is interpreted as
     /// interface name.
-    /// @param pools_list list of pools defined for a subnet
+    /// @param pool_structure a single entry on a list of pools
     /// @throw isc::dhcp::DhcpConfigError when pool parsing fails
-    virtual void build(isc::data::ConstElementPtr pools_list);
+    virtual void build(isc::data::ConstElementPtr pool_structure);
 
     /// @brief Stores the parsed values in a storage provided
     ///        by an upper level parser.
@@ -852,6 +852,61 @@ protected:
     PoolStorage local_pools_;
 };
 
+/// @brief Parser for a list of pools
+///
+/// This parser parses a list pools. Each element on that list gets its own
+/// parser, created with poolParserMaker() method. That method must be specified
+/// for each protocol family (v4 or v6) separately.
+///
+/// This class is not intended to be used directly. Instead, derived classes
+/// should implement poolParserMaker() method.
+class PoolsListParser :  public DhcpConfigParser {
+public:
+
+    /// @brief constructor.
+    ///
+    /// @param dummy first argument is ignored, all Parser constructors
+    /// accept a string as the first argument.
+    /// @param pools is the storage in which to store the parsed pool
+    /// upon "commit".
+    /// @throw isc::dhcp::DhcpConfigError if storage is null.
+    PoolsListParser(const std::string& dummy, PoolStoragePtr pools);
+
+    /// @brief parses the actual structure
+    ///
+    /// This method parses the actual list of pools. It creates a parser
+    /// for each structure using poolParserMaker().
+    ///
+    /// @param pools_list a list of pool structures
+    /// @throw isc::dhcp::DhcpConfigError when pool parsing fails
+    virtual void build(isc::data::ConstElementPtr pools_list);
+
+    /// @brief Stores the parsed values in storage provided
+    ///        by an upper level parser.
+    virtual void commit();
+
+protected:
+
+    /// @brief Creates a PoolParser object
+    ///
+    /// Instantiates appropriate (v4 or v6) PoolParser object.
+    /// @param storage parameter that is passed to ParserMaker() constructor.
+    virtual ParserPtr poolParserMaker(PoolStoragePtr storage) = 0;
+
+    /// @brief pointer to the actual Pools storage
+    ///
+    /// That is typically a storage somewhere in Subnet parser
+    /// (an upper level parser).
+    PoolStoragePtr pools_;
+
+    /// A temporary storage for pools configuration. It is the
+    /// storage where pools are stored by the build function.
+    PoolStoragePtr local_pools_;
+
+    /// Collection of parsers;
+    ParserCollection parsers_;
+};
+
 /// @brief parser for additional relay information
 ///
 /// This concrete parser handles RelayInfo structure defintions.