]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[295-min-max-lease-time-configuration-options] Added lifetime parsing unit tests
authorFrancis Dupont <fdupont@isc.org>
Fri, 17 May 2019 15:54:34 +0000 (17:54 +0200)
committerFrancis Dupont <fdupont@isc.org>
Sat, 22 Jun 2019 14:05:23 +0000 (10:05 -0400)
src/lib/dhcpsrv/tests/cfg_shared_networks4_unittest.cc
src/lib/dhcpsrv/tests/cfg_shared_networks6_unittest.cc
src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc
src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc
src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc

index cd4e7ca468cc525d0dceb79ed8a4a8513afb5892..54b28e5af17ac5903649212cddee25fee894334d 100644 (file)
@@ -191,6 +191,7 @@ TEST(CfgSharedNetworks4Test, duplicateName) {
 TEST(CfgSharedNetworks4Test, unparse) {
     SharedNetwork4Ptr network1(new SharedNetwork4("frog"));
     SharedNetwork4Ptr network2(new SharedNetwork4("dog"));
+    SharedNetwork4Ptr network3(new SharedNetwork4("cat"));
     network1->setIface("eth0");
     network1->addRelayAddress(IOAddress("198.16.1.1"));
     network1->addRelayAddress(IOAddress("198.16.1.2"));
@@ -201,15 +202,27 @@ TEST(CfgSharedNetworks4Test, unparse) {
     network2->setIface("eth1");
     network2->setT1(Triplet<uint32_t>(100));
     network2->setT2(Triplet<uint32_t>(200));
-    network2->setValid(Triplet<uint32_t>(300));
+    network2->setValid(Triplet<uint32_t>(200, 300, 400));
+
+    network3->setIface("eth2");
+    network3->setValid(Triplet<uint32_t>(100));
 
     CfgSharedNetworks4 cfg;
     ASSERT_NO_THROW(cfg.add(network1));
     ASSERT_NO_THROW(cfg.add(network2));
+    ASSERT_NO_THROW(cfg.add(network3));
 
     std::string expected =
         "[\n"
         "  {\n"
+        "    \"interface\": \"eth2\",\n"
+        "    \"name\": \"cat\",\n"
+        "    \"option-data\": [ ],\n"
+        "    \"relay\": { \"ip-addresses\": [ ] },\n"
+        "    \"subnet4\": [ ],\n"
+        "    \"valid-lifetime\": 100\n"
+        "  },\n"
+        "  {\n"
         "    \"interface\": \"eth1\",\n"
         "    \"name\": \"dog\",\n"
         "    \"rebind-timer\": 200,\n"
@@ -217,7 +230,9 @@ TEST(CfgSharedNetworks4Test, unparse) {
         "    \"renew-timer\": 100,\n"
         "    \"relay\": { \"ip-addresses\": [ ] },\n"
         "    \"subnet4\": [ ],\n"
-        "    \"valid-lifetime\": 300\n"
+        "    \"valid-lifetime\": 300,\n"
+        "    \"min-valid-lifetime\": 200,\n"
+        "    \"max-valid-lifetime\": 400\n"
         "  },\n"
         "  {\n"
         "    \"calculate-tee-times\": true,\n"
index 0f6bd7ce530b58600e3f118eaf1bc1c1f9902321..6d338083fc465a83798f9c1ce6b8b6c0975f17d7 100644 (file)
@@ -192,6 +192,7 @@ TEST(CfgSharedNetworks6Test, duplicateName) {
 TEST(CfgSharedNetworks6Test, unparse) {
     SharedNetwork6Ptr network1(new SharedNetwork6("frog"));
     SharedNetwork6Ptr network2(new SharedNetwork6("dog"));
+    SharedNetwork6Ptr network3(new SharedNetwork6("cat"));
 
     network1->setIface("eth0");
     network1->addRelayAddress(IOAddress("2001:db8:1::1"));
@@ -203,15 +204,34 @@ TEST(CfgSharedNetworks6Test, unparse) {
     network2->setIface("eth1");
     network2->setT1(Triplet<uint32_t>(100));
     network2->setT2(Triplet<uint32_t>(200));
+    network2->setPreferred(Triplet<uint32_t>(200));
     network2->setValid(Triplet<uint32_t>(300));
 
+    network3->setIface("eth2");
+    network3->setPreferred(Triplet<uint32_t>(100,200,300));
+    network3->setValid(Triplet<uint32_t>(200,300,400));
+
     CfgSharedNetworks6 cfg;
     ASSERT_NO_THROW(cfg.add(network1));
     ASSERT_NO_THROW(cfg.add(network2));
+    ASSERT_NO_THROW(cfg.add(network3));
 
     std::string expected =
         "[\n"
         "  {\n"
+        "    \"interface\": \"eth2\",\n"
+        "    \"name\": \"cat\",\n"
+        "    \"option-data\": [ ],\n"
+        "    \"relay\": { \"ip-addresses\": [ ] },\n"
+        "    \"subnet6\": [ ],\n"
+        "    \"preferred-lifetime\": 200,\n"
+        "    \"min-preferred-lifetime\": 100,\n"
+        "    \"max-preferred-lifetime\": 300,\n"
+        "    \"valid-lifetime\": 300,\n"
+        "    \"min-valid-lifetime\": 200,\n"
+        "    \"max-valid-lifetime\": 400\n"
+        "  },\n"
+        "  {\n"
         "    \"interface\": \"eth1\",\n"
         "    \"name\": \"dog\",\n"
         "    \"option-data\": [ ],\n"
@@ -219,6 +239,7 @@ TEST(CfgSharedNetworks6Test, unparse) {
         "    \"relay\": { \"ip-addresses\": [ ] },\n"
         "    \"renew-timer\": 100,\n"
         "    \"subnet6\": [ ],\n"
+        "    \"preferred-lifetime\": 200,\n"
         "    \"valid-lifetime\": 300\n"
         "  },\n"
         "  {\n"
index 85ec286e6f48afc01fdc64050bf43011c649decd..c033f04d076493caef711cdb719cffee531b571d 100644 (file)
@@ -1016,6 +1016,7 @@ TEST(CfgSubnets4Test, unparseSubnet) {
 
     subnet2->setIface("lo");
     subnet2->addRelayAddress(IOAddress("10.0.0.1"));
+    subnet2->setValid(Triplet<uint32_t>(100));
 
     subnet3->setIface("eth1");
     subnet3->requireClientClass("foo");
@@ -1029,6 +1030,7 @@ TEST(CfgSubnets4Test, unparseSubnet) {
     subnet3->setSiaddr(IOAddress("192.0.2.2"));
     subnet3->setSname("frog");
     subnet3->setFilename("/dev/null");
+    subnet3->setValid(Triplet<uint32_t>(100, 200, 300));
 
     data::ElementPtr ctx1 = data::Element::fromJSON("{ \"comment\": \"foo\" }");
     subnet1->setContext(ctx1);
@@ -1064,7 +1066,7 @@ TEST(CfgSubnets4Test, unparseSubnet) {
         "    \"renew-timer\": 1,\n"
         "    \"rebind-timer\": 2,\n"
         "    \"relay\": { \"ip-addresses\": [ \"10.0.0.1\" ] },\n"
-        "    \"valid-lifetime\": 3,\n"
+        "    \"valid-lifetime\": 100,\n"
         "    \"4o6-interface\": \"\",\n"
         "    \"4o6-interface-id\": \"\",\n"
         "    \"4o6-subnet\": \"\",\n"
@@ -1082,7 +1084,9 @@ TEST(CfgSubnets4Test, unparseSubnet) {
         "    \"renew-timer\": 1,\n"
         "    \"rebind-timer\": 2,\n"
         "    \"relay\": { \"ip-addresses\": [ ] },\n"
-        "    \"valid-lifetime\": 3,\n"
+        "    \"valid-lifetime\": 200,\n"
+        "    \"min-valid-lifetime\": 100,\n"
+        "    \"max-valid-lifetime\": 300,\n"
         "    \"4o6-interface\": \"\",\n"
         "    \"4o6-interface-id\": \"\",\n"
         "    \"4o6-subnet\": \"\",\n"
@@ -1295,4 +1299,247 @@ TEST(CfgSubnets4Test, teeTimePercentValidation) {
     }
 }
 
+// This test verifies the Subnet4 parser's validation logic for
+// valid-lifetime and indirectly shared lifetime parsing.
+TEST(CfgSubnets4Test, validLifetimeValidation) {
+    // First we create a set of elements that provides all
+    // required for a Subnet4.
+    std::string json =
+        "        {"
+        "            \"id\": 1,\n"
+        "            \"subnet\": \"10.1.2.0/24\", \n"
+        "            \"interface\": \"\", \n"
+        "            \"renew-timer\": 100, \n"
+        "            \"rebind-timer\": 200, \n"
+        "            \"match-client-id\": false, \n"
+        "            \"authoritative\": false, \n"
+        "            \"next-server\": \"\", \n"
+        "            \"server-hostname\": \"\", \n"
+        "            \"boot-file-name\": \"\", \n"
+        "            \"client-class\": \"\", \n"
+        "            \"require-client-classes\": [] \n,"
+        "            \"reservation-mode\": \"all\", \n"
+        "            \"4o6-interface\": \"\", \n"
+        "            \"4o6-interface-id\": \"\", \n"
+        "            \"4o6-subnet\": \"\", \n"
+        "            \"dhcp4o6-port\": 0, \n"
+        "            \"decline-probation-period\": 86400 \n"
+        "        }";
+
+
+    data::ElementPtr elems;
+    ASSERT_NO_THROW(elems = data::Element::fromJSON(json))
+                    << "invalid JSON:" << json << "\n test is broken";
+
+    {
+        SCOPED_TRACE("no valid-lifetime");
+
+        data::ElementPtr copied = data::copy(elems);
+        Subnet4Ptr subnet;
+        Subnet4ConfigParser parser;
+        ASSERT_NO_THROW(subnet = parser.parse(copied));
+        ASSERT_TRUE(subnet);
+        EXPECT_TRUE(subnet->getValid().unspecified());
+        data::ConstElementPtr repr = subnet->toElement();
+        EXPECT_FALSE(repr->get("valid-lifetime"));
+        EXPECT_FALSE(repr->get("min-valid-lifetime"));
+        EXPECT_FALSE(repr->get("max-valid-lifetime"));
+    }
+
+    {
+        SCOPED_TRACE("valid-lifetime only");
+
+        data::ElementPtr copied = data::copy(elems);
+        copied->set("valid-lifetime", data::Element::create(100));
+        Subnet4Ptr subnet;
+        Subnet4ConfigParser parser;
+        ASSERT_NO_THROW(subnet = parser.parse(copied));
+        ASSERT_TRUE(subnet);
+        EXPECT_FALSE(subnet->getValid().unspecified());
+        EXPECT_EQ(100, subnet->getValid());
+        EXPECT_EQ(100, subnet->getValid().getMin());
+        EXPECT_EQ(100, subnet->getValid().getMax());
+        data::ConstElementPtr repr = subnet->toElement();
+        data::ConstElementPtr value = repr->get("valid-lifetime");
+        ASSERT_TRUE(value);
+        EXPECT_EQ("100", value->str());
+        EXPECT_FALSE(repr->get("min-valid-lifetime"));
+        EXPECT_FALSE(repr->get("max-valid-lifetime"));
+    }
+
+    {
+        SCOPED_TRACE("min-valid-lifetime only");
+        data::ElementPtr copied = data::copy(elems);
+        copied->set("min-valid-lifetime", data::Element::create(100));
+        Subnet4Ptr subnet;
+        Subnet4ConfigParser parser;
+        ASSERT_NO_THROW(subnet = parser.parse(copied));
+        ASSERT_TRUE(subnet);
+        EXPECT_FALSE(subnet->getValid().unspecified());
+        EXPECT_EQ(100, subnet->getValid());
+        EXPECT_EQ(100, subnet->getValid().getMin());
+        EXPECT_EQ(100, subnet->getValid().getMax());
+        data::ConstElementPtr repr = subnet->toElement();
+        data::ConstElementPtr value = repr->get("valid-lifetime");
+        ASSERT_TRUE(value);
+        EXPECT_EQ("100", value->str());
+        // Bound only: forgot it was a bound.
+        EXPECT_FALSE(repr->get("min-valid-lifetime"));
+        EXPECT_FALSE(repr->get("max-valid-lifetime"));
+    }
+
+    {
+        SCOPED_TRACE("max-valid-lifetime only");
+        data::ElementPtr copied = data::copy(elems);
+        copied->set("max-valid-lifetime", data::Element::create(100));
+        Subnet4Ptr subnet;
+        Subnet4ConfigParser parser;
+        ASSERT_NO_THROW(subnet = parser.parse(copied));
+        ASSERT_TRUE(subnet);
+        EXPECT_FALSE(subnet->getValid().unspecified());
+        EXPECT_EQ(100, subnet->getValid());
+        EXPECT_EQ(100, subnet->getValid().getMin());
+        EXPECT_EQ(100, subnet->getValid().getMax());
+        data::ConstElementPtr repr = subnet->toElement();
+        data::ConstElementPtr value = repr->get("valid-lifetime");
+        ASSERT_TRUE(value);
+        EXPECT_EQ("100", value->str());
+        // Bound only: forgot it was a bound.
+        EXPECT_FALSE(repr->get("min-valid-lifetime"));
+        EXPECT_FALSE(repr->get("max-valid-lifetime"));
+    }
+
+    {
+        SCOPED_TRACE("min-valid-lifetime and valid-lifetime");
+        data::ElementPtr copied = data::copy(elems);
+        copied->set("min-valid-lifetime", data::Element::create(100));
+        // Use a different (and greater) value for the default.
+        copied->set("valid-lifetime", data::Element::create(200));
+        Subnet4Ptr subnet;
+        Subnet4ConfigParser parser;
+        ASSERT_NO_THROW(subnet = parser.parse(copied));
+        ASSERT_TRUE(subnet);
+        EXPECT_FALSE(subnet->getValid().unspecified());
+        EXPECT_EQ(200, subnet->getValid());
+        EXPECT_EQ(100, subnet->getValid().getMin());
+        EXPECT_EQ(200, subnet->getValid().getMax());
+        data::ConstElementPtr repr = subnet->toElement();
+        data::ConstElementPtr value = repr->get("valid-lifetime");
+        ASSERT_TRUE(value);
+        EXPECT_EQ("200", value->str());
+        data::ConstElementPtr min_value = repr->get("min-valid-lifetime");
+        ASSERT_TRUE(min_value);
+        EXPECT_EQ("100", min_value->str());
+        EXPECT_FALSE(repr->get("max-valid-lifetime"));
+    }
+
+    {
+        SCOPED_TRACE("max-valid-lifetime and valid-lifetime");
+        data::ElementPtr copied = data::copy(elems);
+        copied->set("max-valid-lifetime", data::Element::create(200));
+        // Use a different (and smaller) value for the default.
+        copied->set("valid-lifetime", data::Element::create(100));
+        Subnet4Ptr subnet;
+        Subnet4ConfigParser parser;
+        ASSERT_NO_THROW(subnet = parser.parse(copied));
+        ASSERT_TRUE(subnet);
+        EXPECT_FALSE(subnet->getValid().unspecified());
+        EXPECT_EQ(100, subnet->getValid());
+        EXPECT_EQ(100, subnet->getValid().getMin());
+        EXPECT_EQ(200, subnet->getValid().getMax());
+        data::ConstElementPtr repr = subnet->toElement();
+        data::ConstElementPtr value = repr->get("valid-lifetime");
+        ASSERT_TRUE(value);
+        EXPECT_EQ("100", value->str());
+        data::ConstElementPtr max_value = repr->get("max-valid-lifetime");
+        ASSERT_TRUE(max_value);
+        EXPECT_EQ("200", max_value->str());
+        EXPECT_FALSE(repr->get("min-valid-lifetime"));
+    }
+
+    {
+        SCOPED_TRACE("min-valid-lifetime and max-valid-lifetime");
+        data::ElementPtr copied = data::copy(elems);
+        copied->set("min-valid-lifetime", data::Element::create(100));
+        copied->set("max-valid-lifetime", data::Element::create(200));
+        Subnet4ConfigParser parser;
+        // No idea about the value to use for the default so failing.
+        ASSERT_THROW(parser.parse(copied), DhcpConfigError);
+    }
+
+    {
+        SCOPED_TRACE("all 3 (min, max and default) valid-lifetime");
+        data::ElementPtr copied = data::copy(elems);
+        copied->set("min-valid-lifetime", data::Element::create(100));
+        // Use a different (and greater) value for the default.
+        copied->set("valid-lifetime", data::Element::create(200));
+        // Use a different (and greater than both) value for max.
+        copied->set("max-valid-lifetime", data::Element::create(300));
+        Subnet4Ptr subnet;
+        Subnet4ConfigParser parser;
+        ASSERT_NO_THROW(subnet = parser.parse(copied));
+        ASSERT_TRUE(subnet);
+        EXPECT_FALSE(subnet->getValid().unspecified());
+        EXPECT_EQ(200, subnet->getValid());
+        EXPECT_EQ(100, subnet->getValid().getMin());
+        EXPECT_EQ(300, subnet->getValid().getMax());
+        data::ConstElementPtr repr = subnet->toElement();
+        data::ConstElementPtr value = repr->get("valid-lifetime");
+        ASSERT_TRUE(value);
+        EXPECT_EQ("200", value->str());
+        data::ConstElementPtr min_value = repr->get("min-valid-lifetime");
+        ASSERT_TRUE(min_value);
+        EXPECT_EQ("100", min_value->str());
+        data::ConstElementPtr max_value = repr->get("max-valid-lifetime");
+        ASSERT_TRUE(max_value);
+        EXPECT_EQ("300", max_value->str());
+    }
+
+    {
+        SCOPED_TRACE("default value too small");
+        data::ElementPtr copied = data::copy(elems);
+        copied->set("min-valid-lifetime", data::Element::create(200));
+        // 100 < 200 so it will fail.
+        copied->set("valid-lifetime", data::Element::create(100));
+        // Use a different (and greater than both) value for max.
+        copied->set("max-valid-lifetime", data::Element::create(300));
+        Subnet4ConfigParser parser;
+        ASSERT_THROW(parser.parse(copied), DhcpConfigError);
+    }
+
+    {
+        SCOPED_TRACE("default value too large");
+        data::ElementPtr copied = data::copy(elems);
+        copied->set("min-valid-lifetime", data::Element::create(100));
+        // Use a different (and greater) value for the default.
+        copied->set("valid-lifetime", data::Element::create(300));
+        // 300 > 200 so it will fail.
+        copied->set("max-valid-lifetime", data::Element::create(200));
+        Subnet4ConfigParser parser;
+        ASSERT_THROW(parser.parse(copied), DhcpConfigError);
+    }
+
+    {
+        SCOPED_TRACE("equal bounds are ignored");
+        data::ElementPtr copied = data::copy(elems);
+        copied->set("min-valid-lifetime", data::Element::create(100));
+        copied->set("valid-lifetime", data::Element::create(100));
+        copied->set("max-valid-lifetime", data::Element::create(100));
+        Subnet4Ptr subnet;
+        Subnet4ConfigParser parser;
+        ASSERT_NO_THROW(subnet = parser.parse(copied));
+        ASSERT_TRUE(subnet);
+        EXPECT_FALSE(subnet->getValid().unspecified());
+        EXPECT_EQ(100, subnet->getValid());
+        EXPECT_EQ(100, subnet->getValid().getMin());
+        EXPECT_EQ(100, subnet->getValid().getMax());
+        data::ConstElementPtr repr = subnet->toElement();
+        data::ConstElementPtr value = repr->get("valid-lifetime");
+        ASSERT_TRUE(value);
+        EXPECT_EQ("100", value->str());
+        EXPECT_FALSE(repr->get("min-valid-lifetime"));
+        EXPECT_FALSE(repr->get("max-valid-lifetime"));
+    }
+}
+
 } // end of anonymous namespace
index c37a79f899723dbd50511492fb61f023979c8c57..a0b5c8f3027efa172edbcfcf3d23ab88122491cb 100644 (file)
@@ -616,6 +616,9 @@ TEST(CfgSubnets6Test, unparseSubnet) {
 
     subnet2->setIface("lo");
     subnet2->addRelayAddress(IOAddress("2001:db8:ff::2"));
+    subnet2->setValid(Triplet<uint32_t>(200));
+    subnet2->setPreferred(Triplet<uint32_t>(100));
+
     subnet3->setIface("eth1");
     subnet3->requireClientClass("foo");
     subnet3->requireClientClass("bar");
@@ -624,6 +627,8 @@ TEST(CfgSubnets6Test, unparseSubnet) {
     subnet3->setCalculateTeeTimes(true);
     subnet3->setT1Percent(0.50);
     subnet3->setT2Percent(0.65);
+    subnet3->setValid(Triplet<uint32_t>(100, 200, 300));
+    subnet3->setPreferred(Triplet<uint32_t>(50, 100, 150));
 
     data::ElementPtr ctx1 = data::Element::fromJSON("{ \"comment\": \"foo\" }");
     subnet1->setContext(ctx1);
@@ -659,8 +664,8 @@ TEST(CfgSubnets6Test, unparseSubnet) {
         "    \"renew-timer\": 1,\n"
         "    \"rebind-timer\": 2,\n"
         "    \"relay\": { \"ip-addresses\": [ \"2001:db8:ff::2\" ] },\n"
-        "    \"preferred-lifetime\": 3,\n"
-        "    \"valid-lifetime\": 4,\n"
+        "    \"preferred-lifetime\": 100,\n"
+        "    \"valid-lifetime\": 200,\n"
         "    \"user-context\": { },\n"
         "    \"pools\": [ ],\n"
         "    \"pd-pools\": [ ],\n"
@@ -672,8 +677,12 @@ TEST(CfgSubnets6Test, unparseSubnet) {
         "    \"renew-timer\": 1,\n"
         "    \"rebind-timer\": 2,\n"
         "    \"relay\": { \"ip-addresses\": [ ] },\n"
-        "    \"preferred-lifetime\": 3,\n"
-        "    \"valid-lifetime\": 4,\n"
+        "    \"preferred-lifetime\": 100,\n"
+        "    \"min-preferred-lifetime\": 50,\n"
+        "    \"max-preferred-lifetime\": 150,\n"
+        "    \"valid-lifetime\": 200,\n"
+        "    \"min-valid-lifetime\": 100,\n"
+        "    \"max-valid-lifetime\": 300,\n"
         "    \"rapid-commit\": false,\n"
         "    \"reservation-mode\": \"all\",\n"
         "    \"pools\": [ ],\n"
@@ -1087,4 +1096,244 @@ TEST(CfgSubnets6Test, teeTimePercentValidation) {
     }
 }
 
+// This test verifies the Subnet6 parser's validation logic for
+// preferred-lifetime and indirectly shared lifetime parsing.
+// Note the valid-lifetime stuff is similar and already done for Subnet4.
+TEST(CfgSubnets6Test, preferredLifetimeValidation) {
+    // First we create a set of elements that provides all
+    // required for a Subnet6.
+    std::string json =
+        "        {"
+        "            \"id\": 1,\n"
+        "            \"subnet\": \"2001:db8:1::/64\", \n"
+        "            \"interface\": \"\", \n"
+        "            \"renew-timer\": 100, \n"
+        "            \"rebind-timer\": 200, \n"
+        "            \"valid-lifetime\": 300, \n"
+        "            \"client-class\": \"\", \n"
+        "            \"require-client-classes\": [] \n,"
+        "            \"reservation-mode\": \"all\", \n"
+        "            \"4o6-interface\": \"\", \n"
+        "            \"4o6-interface-id\": \"\", \n"
+        "            \"4o6-subnet\": \"\", \n"
+        "            \"dhcp4o6-port\": 0, \n"
+        "            \"decline-probation-period\": 86400 \n"
+        "        }";
+
+
+    data::ElementPtr elems;
+    ASSERT_NO_THROW(elems = data::Element::fromJSON(json))
+                    << "invalid JSON:" << json << "\n test is broken";
+
+    {
+        SCOPED_TRACE("no preferred-lifetime");
+
+        data::ElementPtr copied = data::copy(elems);
+        Subnet6Ptr subnet;
+        Subnet6ConfigParser parser;
+        ASSERT_NO_THROW(subnet = parser.parse(copied));
+        ASSERT_TRUE(subnet);
+        EXPECT_TRUE(subnet->getPreferred().unspecified());
+        data::ConstElementPtr repr = subnet->toElement();
+        EXPECT_FALSE(repr->get("preferred-lifetime"));
+        EXPECT_FALSE(repr->get("min-preferred-lifetime"));
+        EXPECT_FALSE(repr->get("max-preferred-lifetime"));
+    }
+
+    {
+        SCOPED_TRACE("preferred-lifetime only");
+
+        data::ElementPtr copied = data::copy(elems);
+        copied->set("preferred-lifetime", data::Element::create(100));
+        Subnet6Ptr subnet;
+        Subnet6ConfigParser parser;
+        ASSERT_NO_THROW(subnet = parser.parse(copied));
+        ASSERT_TRUE(subnet);
+        EXPECT_FALSE(subnet->getPreferred().unspecified());
+        EXPECT_EQ(100, subnet->getPreferred());
+        EXPECT_EQ(100, subnet->getPreferred().getMin());
+        EXPECT_EQ(100, subnet->getPreferred().getMax());
+        data::ConstElementPtr repr = subnet->toElement();
+        data::ConstElementPtr value = repr->get("preferred-lifetime");
+        ASSERT_TRUE(value);
+        EXPECT_EQ("100", value->str());
+        EXPECT_FALSE(repr->get("min-preferred-lifetime"));
+        EXPECT_FALSE(repr->get("max-preferred-lifetime"));
+    }
+
+    {
+        SCOPED_TRACE("min-preferred-lifetime only");
+        data::ElementPtr copied = data::copy(elems);
+        copied->set("min-preferred-lifetime", data::Element::create(100));
+        Subnet6Ptr subnet;
+        Subnet6ConfigParser parser;
+        ASSERT_NO_THROW(subnet = parser.parse(copied));
+        ASSERT_TRUE(subnet);
+        EXPECT_FALSE(subnet->getPreferred().unspecified());
+        EXPECT_EQ(100, subnet->getPreferred());
+        EXPECT_EQ(100, subnet->getPreferred().getMin());
+        EXPECT_EQ(100, subnet->getPreferred().getMax());
+        data::ConstElementPtr repr = subnet->toElement();
+        data::ConstElementPtr value = repr->get("preferred-lifetime");
+        ASSERT_TRUE(value);
+        EXPECT_EQ("100", value->str());
+        // Bound only: forgot it was a bound.
+        EXPECT_FALSE(repr->get("min-preferred-lifetime"));
+        EXPECT_FALSE(repr->get("max-preferred-lifetime"));
+    }
+
+    {
+        SCOPED_TRACE("max-preferred-lifetime only");
+        data::ElementPtr copied = data::copy(elems);
+        copied->set("max-preferred-lifetime", data::Element::create(100));
+        Subnet6Ptr subnet;
+        Subnet6ConfigParser parser;
+        ASSERT_NO_THROW(subnet = parser.parse(copied));
+        ASSERT_TRUE(subnet);
+        EXPECT_FALSE(subnet->getPreferred().unspecified());
+        EXPECT_EQ(100, subnet->getPreferred());
+        EXPECT_EQ(100, subnet->getPreferred().getMin());
+        EXPECT_EQ(100, subnet->getPreferred().getMax());
+        data::ConstElementPtr repr = subnet->toElement();
+        data::ConstElementPtr value = repr->get("preferred-lifetime");
+        ASSERT_TRUE(value);
+        EXPECT_EQ("100", value->str());
+        // Bound only: forgot it was a bound.
+        EXPECT_FALSE(repr->get("min-preferred-lifetime"));
+        EXPECT_FALSE(repr->get("max-preferred-lifetime"));
+    }
+
+    {
+        SCOPED_TRACE("min-preferred-lifetime and preferred-lifetime");
+        data::ElementPtr copied = data::copy(elems);
+        copied->set("min-preferred-lifetime", data::Element::create(100));
+        // Use a different (and greater) value for the default.
+        copied->set("preferred-lifetime", data::Element::create(200));
+        Subnet6Ptr subnet;
+        Subnet6ConfigParser parser;
+        ASSERT_NO_THROW(subnet = parser.parse(copied));
+        ASSERT_TRUE(subnet);
+        EXPECT_FALSE(subnet->getPreferred().unspecified());
+        EXPECT_EQ(200, subnet->getPreferred());
+        EXPECT_EQ(100, subnet->getPreferred().getMin());
+        EXPECT_EQ(200, subnet->getPreferred().getMax());
+        data::ConstElementPtr repr = subnet->toElement();
+        data::ConstElementPtr value = repr->get("preferred-lifetime");
+        ASSERT_TRUE(value);
+        EXPECT_EQ("200", value->str());
+        data::ConstElementPtr min_value = repr->get("min-preferred-lifetime");
+        ASSERT_TRUE(min_value);
+        EXPECT_EQ("100", min_value->str());
+        EXPECT_FALSE(repr->get("max-preferred-lifetime"));
+    }
+
+    {
+        SCOPED_TRACE("max-preferred-lifetime and preferred-lifetime");
+        data::ElementPtr copied = data::copy(elems);
+        copied->set("max-preferred-lifetime", data::Element::create(200));
+        // Use a different (and smaller) value for the default.
+        copied->set("preferred-lifetime", data::Element::create(100));
+        Subnet6Ptr subnet;
+        Subnet6ConfigParser parser;
+        ASSERT_NO_THROW(subnet = parser.parse(copied));
+        ASSERT_TRUE(subnet);
+        EXPECT_FALSE(subnet->getPreferred().unspecified());
+        EXPECT_EQ(100, subnet->getPreferred());
+        EXPECT_EQ(100, subnet->getPreferred().getMin());
+        EXPECT_EQ(200, subnet->getPreferred().getMax());
+        data::ConstElementPtr repr = subnet->toElement();
+        data::ConstElementPtr value = repr->get("preferred-lifetime");
+        ASSERT_TRUE(value);
+        EXPECT_EQ("100", value->str());
+        data::ConstElementPtr max_value = repr->get("max-preferred-lifetime");
+        ASSERT_TRUE(max_value);
+        EXPECT_EQ("200", max_value->str());
+        EXPECT_FALSE(repr->get("min-preferred-lifetime"));
+    }
+
+    {
+        SCOPED_TRACE("min-preferred-lifetime and max-preferred-lifetime");
+        data::ElementPtr copied = data::copy(elems);
+        copied->set("min-preferred-lifetime", data::Element::create(100));
+        copied->set("max-preferred-lifetime", data::Element::create(200));
+        Subnet6ConfigParser parser;
+        // No idea about the value to use for the default so failing.
+        ASSERT_THROW(parser.parse(copied), DhcpConfigError);
+    }
+
+    {
+        SCOPED_TRACE("all 3 (min, max and default) preferred-lifetime");
+        data::ElementPtr copied = data::copy(elems);
+        copied->set("min-preferred-lifetime", data::Element::create(100));
+        // Use a different (and greater) value for the default.
+        copied->set("preferred-lifetime", data::Element::create(200));
+        // Use a different (and greater than both) value for max.
+        copied->set("max-preferred-lifetime", data::Element::create(300));
+        Subnet6Ptr subnet;
+        Subnet6ConfigParser parser;
+        ASSERT_NO_THROW(subnet = parser.parse(copied));
+        ASSERT_TRUE(subnet);
+        EXPECT_FALSE(subnet->getPreferred().unspecified());
+        EXPECT_EQ(200, subnet->getPreferred());
+        EXPECT_EQ(100, subnet->getPreferred().getMin());
+        EXPECT_EQ(300, subnet->getPreferred().getMax());
+        data::ConstElementPtr repr = subnet->toElement();
+        data::ConstElementPtr value = repr->get("preferred-lifetime");
+        ASSERT_TRUE(value);
+        EXPECT_EQ("200", value->str());
+        data::ConstElementPtr min_value = repr->get("min-preferred-lifetime");
+        ASSERT_TRUE(min_value);
+        EXPECT_EQ("100", min_value->str());
+        data::ConstElementPtr max_value = repr->get("max-preferred-lifetime");
+        ASSERT_TRUE(max_value);
+        EXPECT_EQ("300", max_value->str());
+    }
+
+    {
+        SCOPED_TRACE("default value too small");
+        data::ElementPtr copied = data::copy(elems);
+        copied->set("min-preferred-lifetime", data::Element::create(200));
+        // 100 < 200 so it will fail.
+        copied->set("preferred-lifetime", data::Element::create(100));
+        // Use a different (and greater than both) value for max.
+        copied->set("max-preferred-lifetime", data::Element::create(300));
+        Subnet6ConfigParser parser;
+        ASSERT_THROW(parser.parse(copied), DhcpConfigError);
+    }
+
+    {
+        SCOPED_TRACE("default value too large");
+        data::ElementPtr copied = data::copy(elems);
+        copied->set("min-preferred-lifetime", data::Element::create(100));
+        // Use a different (and greater) value for the default.
+        copied->set("preferred-lifetime", data::Element::create(300));
+        // 300 > 200 so it will fail.
+        copied->set("max-preferred-lifetime", data::Element::create(200));
+        Subnet6ConfigParser parser;
+        ASSERT_THROW(parser.parse(copied), DhcpConfigError);
+    }
+
+    {
+        SCOPED_TRACE("equal bounds are ignored");
+        data::ElementPtr copied = data::copy(elems);
+        copied->set("min-preferred-lifetime", data::Element::create(100));
+        copied->set("preferred-lifetime", data::Element::create(100));
+        copied->set("max-preferred-lifetime", data::Element::create(100));
+        Subnet6Ptr subnet;
+        Subnet6ConfigParser parser;
+        ASSERT_NO_THROW(subnet = parser.parse(copied));
+        ASSERT_TRUE(subnet);
+        EXPECT_FALSE(subnet->getPreferred().unspecified());
+        EXPECT_EQ(100, subnet->getPreferred());
+        EXPECT_EQ(100, subnet->getPreferred().getMin());
+        EXPECT_EQ(100, subnet->getPreferred().getMax());
+        data::ConstElementPtr repr = subnet->toElement();
+        data::ConstElementPtr value = repr->get("preferred-lifetime");
+        ASSERT_TRUE(value);
+        EXPECT_EQ("100", value->str());
+        EXPECT_FALSE(repr->get("min-preferred-lifetime"));
+        EXPECT_FALSE(repr->get("max-preferred-lifetime"));
+    }
+}
+
 } // end of anonymous namespace
index 9c007727adeb87e668c9c4927a333e078d355f66..82744d88fcabb10f7ee53ef8f755a852742107b5 100644 (file)
@@ -130,6 +130,8 @@ public:
                 "    \"require-client-classes\": [ \"runner\" ],"
                 "    \"user-context\": { \"comment\": \"example\" },"
                 "    \"valid-lifetime\": 399,"
+                "    \"min-valid-lifetime\": 299,"
+                "    \"max-valid-lifetime\": 499,"
                 "    \"calculate-tee-times\": true,"
                 "    \"t1-percent\": 0.345,"
                 "    \"t2-percent\": 0.721,"
@@ -147,6 +149,8 @@ public:
                 "            \"renew-timer\": 100,"
                 "            \"rebind-timer\": 200,"
                 "            \"valid-lifetime\": 300,"
+                "            \"min-valid-lifetime\": 200,"
+                "            \"max-valid-lifetime\": 400,"
                 "            \"match-client-id\": false,"
                 "            \"authoritative\": false,"
                 "            \"next-server\": \"\","
@@ -237,6 +241,8 @@ TEST_F(SharedNetwork4ParserTest, parse) {
     EXPECT_EQ(99, network->getT1());
     EXPECT_EQ(199, network->getT2());
     EXPECT_EQ(399, network->getValid());
+    EXPECT_EQ(299, network->getValid().getMin());
+    EXPECT_EQ(499, network->getValid().getMax());
     EXPECT_TRUE(network->getCalculateTeeTimes());
     EXPECT_EQ(0.345, network->getT1Percent());
     EXPECT_EQ(0.721, network->getT2Percent());
@@ -264,11 +270,17 @@ TEST_F(SharedNetwork4ParserTest, parse) {
     Subnet4Ptr subnet1 = network->getSubnet(SubnetID(1));
     ASSERT_TRUE(subnet1);
     EXPECT_EQ("10.1.2.0", subnet1->get().first.toText());
+    EXPECT_EQ(300, subnet1->getValid());
+    EXPECT_EQ(200, subnet1->getValid().getMin());
+    EXPECT_EQ(400, subnet1->getValid().getMax());
 
     // Subnet with id 2
     Subnet4Ptr subnet2 = network->getSubnet(SubnetID(2));
     ASSERT_TRUE(subnet2);
     EXPECT_EQ("192.0.2.0", subnet2->get().first.toText());
+    EXPECT_EQ(30, subnet2->getValid());
+    EXPECT_EQ(30, subnet2->getValid().getMin());
+    EXPECT_EQ(30, subnet2->getValid().getMax());
 
     // DHCP options
     ConstCfgOptionPtr cfg_option = network->getCfgOption();
@@ -422,6 +434,8 @@ public:
                 "\"eth1\","
                 "    \"name\": \"bird\","
                 "    \"preferred-lifetime\": 211,"
+                "    \"min-preferred-lifetime\": 111,"
+                "    \"max-preferred-lifetime\": 311,"
                 "    \"rapid-commit\": true,"
                 "    \"rebind-timer\": 199,"
                 "    \"relay\": { \"ip-addresses\": [ \"2001:db8:1::1\" ] },"
@@ -430,6 +444,8 @@ public:
                 "    \"reservation-mode\": \"out-of-pool\","
                 "    \"user-context\": { },"
                 "    \"valid-lifetime\": 399,"
+                "    \"min-valid-lifetime\": 299,"
+                "    \"max-valid-lifetime\": 499,"
                 "    \"calculate-tee-times\": true,"
                 "    \"t1-percent\": 0.345,"
                 "    \"t2-percent\": 0.721,"
@@ -448,7 +464,11 @@ public:
                 "            \"renew-timer\": 100,"
                 "            \"rebind-timer\": 200,"
                 "            \"preferred-lifetime\": 300,"
+                "            \"min-preferred-lifetime\": 200,"
+                "            \"max-preferred-lifetime\": 400,"
                 "            \"valid-lifetime\": 400,"
+                "            \"min-valid-lifetime\": 300,"
+                "            \"max-valid-lifetime\": 500,"
                 "            \"client-class\": \"\","
                 "            \"require-client-classes\": []\n,"
                 "            \"reservation-mode\": \"all\","
@@ -514,10 +534,14 @@ TEST_F(SharedNetwork6ParserTest, parse) {
     EXPECT_EQ("bird", network->getName());
     EXPECT_EQ("eth1", network->getIface().get());
     EXPECT_EQ(211, network->getPreferred());
+    EXPECT_EQ(111, network->getPreferred().getMin());
+    EXPECT_EQ(311, network->getPreferred().getMax());
     EXPECT_TRUE(network->getRapidCommit());
     EXPECT_EQ(99, network->getT1());
     EXPECT_EQ(199, network->getT2());
     EXPECT_EQ(399, network->getValid());
+    EXPECT_EQ(299, network->getValid().getMin());
+    EXPECT_EQ(499, network->getValid().getMax());
     EXPECT_TRUE(network->getCalculateTeeTimes());
     EXPECT_EQ(0.345, network->getT1Percent());
     EXPECT_EQ(0.721, network->getT2Percent());
@@ -541,11 +565,23 @@ TEST_F(SharedNetwork6ParserTest, parse) {
     Subnet6Ptr subnet1 = network->getSubnet(SubnetID(1));
     ASSERT_TRUE(subnet1);
     EXPECT_EQ("3000::", subnet1->get().first.toText());
+    EXPECT_EQ(300, subnet1->getPreferred());
+    EXPECT_EQ(200, subnet1->getPreferred().getMin());
+    EXPECT_EQ(400, subnet1->getPreferred().getMax());
+    EXPECT_EQ(400, subnet1->getValid());
+    EXPECT_EQ(300, subnet1->getValid().getMin());
+    EXPECT_EQ(500, subnet1->getValid().getMax());
 
     // Subnet with id 2
     Subnet6Ptr subnet2 = network->getSubnet(SubnetID(2));
     ASSERT_TRUE(subnet2);
     EXPECT_EQ("2001:db8:1::", subnet2->get().first.toText());
+    EXPECT_EQ(30, subnet2->getPreferred());
+    EXPECT_EQ(30, subnet2->getPreferred().getMin());
+    EXPECT_EQ(30, subnet2->getPreferred().getMax());
+    EXPECT_EQ(40, subnet2->getValid());
+    EXPECT_EQ(40, subnet2->getValid().getMin());
+    EXPECT_EQ(40, subnet2->getValid().getMax());
 
     // DHCP options
     ConstCfgOptionPtr cfg_option = network->getCfgOption();