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

[master] Merge branch 'trac3281'

Conflicts:
	doc/guide/bind10-guide.xml
parents 70f1f297 29dfbd1c
......@@ -3848,6 +3848,42 @@ Dhcp4/dhcp-ddns/qualifying-suffix "example.com" string
</para>
</section>
<section id="ipv4-subnet-id">
<title>IPv4 Subnet Identifier</title>
<para>
Subnet identifier is a unique number associated with a particular subnet.
In principle, it is used to associate clients' leases with respective subnets.
When subnet identifier is not specified for a subnet being configured, it will
be automatically assigned by the configuration mechanism. The identifiers
are assigned from 1 and are monotonically increased for each subsequent
subnet: 1, 2, 3 ....
</para>
<para>
If there are multiple subnets configured with auto-generated identifiers and
one of them is removed, the subnet identifiers may be renumbered. For example:
if there are 4 subnets and 3rd is removed the last subnet will be assigned
identifier that the 3rd subnet had before removal. As a result, the leases
stored in the lease database for subnet 3 are now associated with the
subnet 4, which may have unexpected consequences. In the future it is planned
to implement the mechanism to preserve auto-generated subnet ids upon removal
of one of the subnets. Currently, the only remedy for this issue is to
manually specify the unique subnet identifier for each subnet.
</para>
<para>
The following configuration:
<screen>
&gt; <userinput>config add Dhcp4/subnet4</userinput>
&gt; <userinput>config set Dhcp4/subnet4[0]/subnet "192.0.2.0/24"</userinput>
&gt; <userinput>config set Dhcp4/subnet4[0]/id 1024</userinput>
&gt; <userinput>config commit</userinput>
</screen>
will assign the arbitrary subnet identifier to the newly configured subnet.
This identifier will not change for this subnet until "id" parameter is
removed or set to 0. The value of 0 forces auto-generation of subnet
identifier.
</para>
</section>
<section id="dhcp4-address-config">
<title>Configuration of IPv4 Address Pools</title>
<para>
......@@ -5016,19 +5052,11 @@ Dhcp4/dhcp-ddns/qualifying-suffix "example.com" string
yet</quote>, rather than actual limitations.</para>
<itemizedlist>
<listitem> <!-- see tickets #3234, #3281 -->
<para>
On-line configuration has some limitations. Adding new subnets or
modifying existing ones work, as is removing the last subnet from
the list. However, removing non-last (e.g. removing subnet 1,2 or 3 if
there are 4 subnets configured) will cause issues. The problem is
caused by simplistic subnet-id assignment. The subnets are always
numbered, starting from 1. That subnet-id is then used in leases
that are stored in the lease database. Removing non-last subnet will
cause the configuration information to mismatch data in the lease
database. It is possible to manually update subnet-id fields in
MySQL database, but it is awkward and error prone process. A better
reconfiguration support is planned.
</para>
<simpara>
Removal of a subnet during server reconfiguration may cause renumbering
of auto-generated subnet identifiers, as described in section
<xref linkend="ipv4-subnet-id"/>.
</simpara>
</listitem>
<listitem>
<simpara>
......@@ -5296,6 +5324,42 @@ Dhcp6/dhcp-ddns/qualifying-suffix "example.com" string
</para>
</section>
<section id="ipv6-subnet-id">
<title>IPv6 Subnet Identifier</title>
<para>
Subnet identifier is a unique number associated with a particular subnet.
In principle, it is used to associate clients' leases with respective subnets.
When subnet identifier is not specified for a subnet being configured, it will
be automatically assigned by the configuration mechanism. The identifiers
are assigned from 1 and are monotonically increased for each subsequent
subnet: 1, 2, 3 ....
</para>
<para>
If there are multiple subnets configured with auto-generated identifiers and
one of them is removed, the subnet identifiers may be renumbered. For example:
if there are 4 subnets and 3rd is removed the last subnet will be assigned
identifier that the 3rd subnet had before removal. As a result, the leases
stored in the lease database for subnet 3 are now associated with the
subnet 4, which may have unexpected consequences. In the future it is planned
to implement the mechanism to preserve auto-generated subnet ids upon removal
of one of the subnets. Currently, the only remedy for this issue is to
manually specify the unique subnet identifier for each subnet.
</para>
<para>
The following configuration:
<screen>
&gt; <userinput>config add Dhcp6/subnet6</userinput>
&gt; <userinput>config set Dhcp6/subnet6[0]/subnet "2001:db8:1::/64"</userinput>
&gt; <userinput>config set Dhcp6/subnet6[0]/id 1024</userinput>
&gt; <userinput>config commit</userinput>
</screen>
will assign the arbitrary subnet identifier to the newly configured subnet.
This identifier will not change for this subnet until "id" parameter is
removed or set to 0. The value of 0 forces auto-generation of subnet
identifier.
</para>
</section>
<section id="dhcp6-unicast">
<title>Unicast traffic support</title>
<para>
......@@ -6439,19 +6503,11 @@ Dhcp6/dhcp-ddns/qualifying-suffix "example.com" string
<itemizedlist>
<listitem> <!-- see tickets #3234, #3281 -->
<simpara>
On-line configuration has some limitations. Adding new subnets or
modifying existing ones work, as is removing the last subnet from
the list. However, removing non-last (e.g. removing subnet 1,2 or 3 if
there are 4 subnets configured) will cause issues. The problem is
caused by simplistic subnet-id assignment. The subnets are always
numbered, starting from 1. That subnet-id is then used in leases
that are stored in the lease database. Removing non-last subnet will
cause the configuration information to mismatch data in the lease
database. It is possible to manually update subnet-id fields in
MySQL database, but it is awkward and error prone process. A better
reconfiguration support is planned.
</simpara>
<simpara>
Removal of a subnet during server reconfiguration may cause renumbering
of auto-generated subnet identifiers, as described in section
<xref linkend="ipv6-subnet-id"/>.
</simpara>
</listitem>
<listitem>
<simpara>
......
......@@ -201,7 +201,8 @@ protected:
DhcpConfigParser* parser = NULL;
if ((config_id.compare("valid-lifetime") == 0) ||
(config_id.compare("renew-timer") == 0) ||
(config_id.compare("rebind-timer") == 0)) {
(config_id.compare("rebind-timer") == 0) ||
(config_id.compare("id") == 0)) {
parser = new Uint32Parser(config_id, uint32_values_);
} else if ((config_id.compare("subnet") == 0) ||
(config_id.compare("interface") == 0) ||
......@@ -271,6 +272,10 @@ protected:
Triplet<uint32_t> t1 = getParam("renew-timer");
Triplet<uint32_t> t2 = getParam("rebind-timer");
Triplet<uint32_t> valid = getParam("valid-lifetime");
// Subnet ID is optional. If it is not supplied the value of 0 is used,
// which means autogenerate.
SubnetID subnet_id =
static_cast<SubnetID>(uint32_values_->getOptionalParam("id", 0));
stringstream tmp;
tmp << addr << "/" << (int)len
......@@ -278,7 +283,7 @@ protected:
LOG_INFO(dhcp4_logger, DHCP4_CONFIG_NEW_SUBNET).arg(tmp.str());
Subnet4Ptr subnet4(new Subnet4(addr, len, t1, t2, valid));
Subnet4Ptr subnet4(new Subnet4(addr, len, t1, t2, valid, subnet_id));
subnet_ = subnet4;
// Try global value first
......
......@@ -213,6 +213,12 @@
"item_default": ""
},
{ "item_name": "id",
"item_type": "integer",
"item_optional": false,
"item_default": 0
},
{ "item_name": "renew-timer",
"item_type": "integer",
"item_optional": false,
......
......@@ -550,6 +550,8 @@ TEST_F(Dhcp4ParserTest, subnetGlobalDefaults) {
// multiple times.
TEST_F(Dhcp4ParserTest, multipleSubnets) {
ConstElementPtr x;
// Collection of four subnets for which subnet ids should be
// autogenerated - ids are unspecified or set to 0.
string config = "{ \"interfaces\": [ \"*\" ],"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
......@@ -559,7 +561,8 @@ TEST_F(Dhcp4ParserTest, multipleSubnets) {
" },"
" {"
" \"pool\": [ \"192.0.3.101 - 192.0.3.150\" ],"
" \"subnet\": \"192.0.3.0/24\" "
" \"subnet\": \"192.0.3.0/24\", "
" \"id\": 0 "
" },"
" {"
" \"pool\": [ \"192.0.4.101 - 192.0.4.150\" ],"
......@@ -573,16 +576,9 @@ TEST_F(Dhcp4ParserTest, multipleSubnets) {
ElementPtr json = Element::fromJSON(config);
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
ASSERT_TRUE(x);
comment_ = parseAnswer(rcode_, x);
ASSERT_EQ(0, rcode_);
int cnt = 0; // Number of reconfigurations
do {
ElementPtr json = Element::fromJSON(config);
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
ASSERT_TRUE(x);
comment_ = parseAnswer(rcode_, x);
......@@ -605,6 +601,96 @@ TEST_F(Dhcp4ParserTest, multipleSubnets) {
} while (++cnt < 10);
}
// This test checks that it is possible to assign arbitrary ids for subnets.
TEST_F(Dhcp4ParserTest, multipleSubnetsExplicitIDs) {
ConstElementPtr x;
// Four subnets with arbitrary subnet ids.
string config = "{ \"interfaces\": [ \"*\" ],"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet4\": [ { "
" \"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\" ],"
" \"subnet\": \"192.0.3.0/24\", "
" \"id\": 100 "
" },"
" {"
" \"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\" ],"
" \"subnet\": \"192.0.5.0/24\", "
" \"id\": 34 "
" } ],"
"\"valid-lifetime\": 4000 }";
ElementPtr json = Element::fromJSON(config);
int cnt = 0; // Number of reconfigurations
do {
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
ASSERT_TRUE(x);
comment_ = parseAnswer(rcode_, x);
ASSERT_EQ(0, rcode_);
const Subnet4Collection* subnets = CfgMgr::instance().getSubnets4();
ASSERT_TRUE(subnets);
ASSERT_EQ(4, subnets->size()); // We expect 4 subnets
// Verify that subnet ids are as expected.
EXPECT_EQ(1024, subnets->at(0)->getID());
EXPECT_EQ(100, subnets->at(1)->getID());
EXPECT_EQ(1, subnets->at(2)->getID());
EXPECT_EQ(34, subnets->at(3)->getID());
// Repeat reconfiguration process 10 times and check that the subnet-id
// is set to the same value.
} while (++cnt < 3);
}
// Check that the configuration with two subnets having the same id is rejected.
TEST_F(Dhcp4ParserTest, multipleSubnetsOverlapingIDs) {
ConstElementPtr x;
// Four subnets, two of them having the same id.
string config = "{ \"interfaces\": [ \"*\" ],"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet4\": [ { "
" \"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\" ],"
" \"subnet\": \"192.0.3.0/24\", "
" \"id\": 100 "
" },"
" {"
" \"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\" ],"
" \"subnet\": \"192.0.5.0/24\", "
" \"id\": 34 "
" } ],"
"\"valid-lifetime\": 4000 }";
ElementPtr json = Element::fromJSON(config);
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
ASSERT_TRUE(x);
comment_ = parseAnswer(rcode_, x);
EXPECT_NE(rcode_, 0);
}
// Goal of this test is to verify that a previously configured subnet can be
// deleted in subsequent reconfiguration.
TEST_F(Dhcp4ParserTest, reconfigureRemoveSubnet) {
......@@ -616,19 +702,23 @@ TEST_F(Dhcp4ParserTest, reconfigureRemoveSubnet) {
"\"renew-timer\": 1000, "
"\"subnet4\": [ { "
" \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
" \"subnet\": \"192.0.2.0/24\" "
" \"subnet\": \"192.0.2.0/24\", "
" \"id\": 1 "
" },"
" {"
" \"pool\": [ \"192.0.3.101 - 192.0.3.150\" ],"
" \"subnet\": \"192.0.3.0/24\" "
" \"subnet\": \"192.0.3.0/24\", "
" \"id\": 2 "
" },"
" {"
" \"pool\": [ \"192.0.4.101 - 192.0.4.150\" ],"
" \"subnet\": \"192.0.4.0/24\" "
" \"subnet\": \"192.0.4.0/24\", "
" \"id\": 3 "
" },"
" {"
" \"pool\": [ \"192.0.5.101 - 192.0.5.150\" ],"
" \"subnet\": \"192.0.5.0/24\" "
" \"subnet\": \"192.0.5.0/24\", "
" \"id\": 4 "
" } ],"
"\"valid-lifetime\": 4000 }";
......@@ -638,15 +728,18 @@ TEST_F(Dhcp4ParserTest, reconfigureRemoveSubnet) {
"\"renew-timer\": 1000, "
"\"subnet4\": [ { "
" \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
" \"subnet\": \"192.0.2.0/24\" "
" \"subnet\": \"192.0.2.0/24\", "
" \"id\": 1 "
" },"
" {"
" \"pool\": [ \"192.0.3.101 - 192.0.3.150\" ],"
" \"subnet\": \"192.0.3.0/24\" "
" \"subnet\": \"192.0.3.0/24\", "
" \"id\": 2 "
" },"
" {"
" \"pool\": [ \"192.0.4.101 - 192.0.4.150\" ],"
" \"subnet\": \"192.0.4.0/24\" "
" \"subnet\": \"192.0.4.0/24\", "
" \"id\": 3 "
" } ],"
"\"valid-lifetime\": 4000 }";
......@@ -656,15 +749,18 @@ TEST_F(Dhcp4ParserTest, reconfigureRemoveSubnet) {
"\"renew-timer\": 1000, "
"\"subnet4\": [ { "
" \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
" \"subnet\": \"192.0.2.0/24\" "
" \"subnet\": \"192.0.2.0/24\", "
" \"id\": 1 "
" },"
" {"
" \"pool\": [ \"192.0.4.101 - 192.0.4.150\" ],"
" \"subnet\": \"192.0.4.0/24\" "
" \"subnet\": \"192.0.4.0/24\", "
" \"id\": 3 "
" },"
" {"
" \"pool\": [ \"192.0.5.101 - 192.0.5.150\" ],"
" \"subnet\": \"192.0.5.0/24\" "
" \"subnet\": \"192.0.5.0/24\", "
" \"id\": 4 "
" } ],"
"\"valid-lifetime\": 4000 }";
......@@ -700,7 +796,6 @@ TEST_F(Dhcp4ParserTest, reconfigureRemoveSubnet) {
/// CASE 2: Configure 4 subnets, then reconfigure and remove one
/// from in between (not first, not last)
#if 0
/// @todo: Uncomment subnet removal test as part of #3281.
json = Element::fromJSON(config4);
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
......@@ -723,7 +818,6 @@ TEST_F(Dhcp4ParserTest, reconfigureRemoveSubnet) {
// The second subnet (with subnet-id = 2) is no longer there
EXPECT_EQ(3, subnets->at(1)->getID());
EXPECT_EQ(4, subnets->at(2)->getID());
#endif
}
......
// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
......@@ -406,7 +406,8 @@ protected:
if ((config_id.compare("preferred-lifetime") == 0) ||
(config_id.compare("valid-lifetime") == 0) ||
(config_id.compare("renew-timer") == 0) ||
(config_id.compare("rebind-timer") == 0)) {
(config_id.compare("rebind-timer") == 0) ||
(config_id.compare("id") == 0)) {
parser = new Uint32Parser(config_id, uint32_values_);
} else if ((config_id.compare("subnet") == 0) ||
(config_id.compare("interface") == 0) ||
......@@ -480,6 +481,10 @@ protected:
Triplet<uint32_t> t2 = getParam("rebind-timer");
Triplet<uint32_t> pref = getParam("preferred-lifetime");
Triplet<uint32_t> valid = getParam("valid-lifetime");
// Subnet ID is optional. If it is not supplied the value of 0 is used,
// which means autogenerate.
SubnetID subnet_id =
static_cast<SubnetID>(uint32_values_->getOptionalParam("id", 0));
// Get interface-id option content. For now we support string
// represenation only
......@@ -518,7 +523,8 @@ protected:
LOG_INFO(dhcp6_logger, DHCP6_CONFIG_NEW_SUBNET).arg(tmp.str());
// Create a new subnet.
Subnet6* subnet6 = new Subnet6(addr, len, t1, t2, pref, valid);
Subnet6* subnet6 = new Subnet6(addr, len, t1, t2, pref, valid,
subnet_id);
// Configure interface-id for remote interfaces, if defined
if (!ifaceid.empty()) {
......
......@@ -207,6 +207,12 @@
"item_default": ""
},
{ "item_name": "id",
"item_type": "integer",
"item_optional": false,
"item_default": 0
},
{ "item_name": "interface",
"item_type": "string",
"item_optional": false,
......
......@@ -569,11 +569,10 @@ TEST_F(Dhcp6ParserTest, subnetGlobalDefaults) {
EXPECT_EQ(1, subnet->getID());
}
// Goal of this test is to verify that multiple subnets get unique
// subnet-ids. Also, test checks that it's possible to do reconfiguration
// multiple times.
TEST_F(Dhcp6ParserTest, multipleSubnets) {
ConstElementPtr x;
// Collection of four subnets for which ids should be autogenerated
// - ids are unspecified or set to 0.
string config = "{ \"interfaces\": [ \"*\" ],"
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
......@@ -584,7 +583,8 @@ TEST_F(Dhcp6ParserTest, multipleSubnets) {
" },"
" {"
" \"pool\": [ \"2001:db8:2::/80\" ],"
" \"subnet\": \"2001:db8:2::/64\" "
" \"subnet\": \"2001:db8:2::/64\", "
" \"id\": 0"
" },"
" {"
" \"pool\": [ \"2001:db8:3::/80\" ],"
......@@ -598,9 +598,9 @@ TEST_F(Dhcp6ParserTest, multipleSubnets) {
int cnt = 0; // Number of reconfigurations
do {
ElementPtr json = Element::fromJSON(config);
ElementPtr json = Element::fromJSON(config);
do {
EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
ASSERT_TRUE(x);
comment_ = parseAnswer(rcode_, x);
......@@ -623,6 +623,100 @@ TEST_F(Dhcp6ParserTest, multipleSubnets) {
} while (++cnt < 10);
}
// This checks that it is possible to assign arbitrary ids for subnets.
TEST_F(Dhcp6ParserTest, multipleSubnetsExplicitIDs) {
ConstElementPtr x;
// Four subnets with arbitrary subnet ids.
string config = "{ \"interfaces\": [ \"*\" ],"
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet6\": [ { "
" \"pool\": [ \"2001:db8:1::/80\" ],"
" \"subnet\": \"2001:db8:1::/64\", "
" \"id\": 1024"
" },"
" {"
" \"pool\": [ \"2001:db8:2::/80\" ],"
" \"subnet\": \"2001:db8:2::/64\", "
" \"id\": 100"
" },"
" {"
" \"pool\": [ \"2001:db8:3::/80\" ],"
" \"subnet\": \"2001:db8:3::/64\", "
" \"id\": 1"
" },"
" {"
" \"pool\": [ \"2001:db8:4::/80\" ],"
" \"subnet\": \"2001:db8:4::/64\", "
" \"id\": 34"
" } ],"
"\"valid-lifetime\": 4000 }";
int cnt = 0; // Number of reconfigurations
ElementPtr json = Element::fromJSON(config);
do {
EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
ASSERT_TRUE(x);
comment_ = parseAnswer(rcode_, x);
ASSERT_EQ(0, rcode_);
const Subnet6Collection* subnets = CfgMgr::instance().getSubnets6();
ASSERT_TRUE(subnets);
ASSERT_EQ(4, subnets->size()); // We expect 4 subnets
// Check that subnet ids are as expected.
EXPECT_EQ(1024, subnets->at(0)->getID());
EXPECT_EQ(100, subnets->at(1)->getID());
EXPECT_EQ(1, subnets->at(2)->getID());
EXPECT_EQ(34, subnets->at(3)->getID());
// Repeat reconfiguration process 10 times and check that the subnet-id
// is set to the same value.
} while (++cnt < 3);
}
// CHeck that the configuration with two subnets having the same id is rejected.
TEST_F(Dhcp6ParserTest, multipleSubnetsOverlapingIDs) {
ConstElementPtr x;
// Four subnets, two of them have the same id.
string config = "{ \"interfaces\": [ \"*\" ],"
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet6\": [ { "
" \"pool\": [ \"2001:db8:1::/80\" ],"
" \"subnet\": \"2001:db8:1::/64\", "
" \"id\": 1024"
" },"
" {"
" \"pool\": [ \"2001:db8:2::/80\" ],"
" \"subnet\": \"2001:db8:2::/64\", "
" \"id\": 100"
" },"
" {"
" \"pool\": [ \"2001:db8:3::/80\" ],"
" \"subnet\": \"2001:db8:3::/64\", "
" \"id\": 1024"
" },"
" {"
" \"pool\": [ \"2001:db8:4::/80\" ],"
" \"subnet\": \"2001:db8:4::/64\", "
" \"id\": 34"
" } ],"
"\"valid-lifetime\": 4000 }";
ElementPtr json = Element::fromJSON(config);
EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
ASSERT_TRUE(x);
comment_ = parseAnswer(rcode_, x);
ASSERT_NE(rcode_, 0);
}
// Goal of this test is to verify that a previously configured subnet can be
// deleted in subsequent reconfiguration.
TEST_F(Dhcp6ParserTest, reconfigureRemoveSubnet) {
......@@ -635,19 +729,23 @@ TEST_F(Dhcp6ParserTest, reconfigureRemoveSubnet) {
"\"renew-timer\": 1000, "
"\"subnet6\": [ { "
" \"pool\": [ \"2001:db8:1::/80\" ],"
" \"subnet\": \"2001:db8:1::/64\" "
" \"subnet\": \"2001:db8:1::/64\", "
" \"id\": 1"
" },"
" {"
" \"pool\": [ \"2001:db8:2::/80\" ],"
" \"subnet\": \"2001:db8:2::/64\" "
" \"subnet\": \"2001:db8:2::/64\", "
" \"id\": 2"
" },"
" {"
" \"pool\": [ \"2001:db8:3::/80\" ],"
" \"subnet\": \"2001:db8:3::/64\" "
" \"subnet\": \"2001:db8:3::/64\", "
" \"id\": 3"
" },"
" {"
" \"pool\": [ \"2001:db8:4::/80\" ],"
" \"subnet\": \"2001:db8:4::/64\" "
" \"subnet\": \"2001:db8:4::/64\", "
" \"id\": 4"
" } ],"