return (expired);
}
+namespace {
+void sanitizeLifetimes6(AllocEngine::ClientContext6& ctx,
+ uint32_t& preferred, uint32_t& valid) {
+ // If preferred isn't set or insane, calculate it as valid_lft * 0.625.
+ if (!preferred || preferred > valid) {
+ preferred = ((valid * 5)/8);
+ LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
+ ALLOC_ENGINE_V6_CALCULATED_PREFERRED_LIFETIME)
+ .arg(ctx.query_->getLabel())
+ .arg(preferred);
+ }
+}
+} // end of anonymous namespace.
+
void
AllocEngine::getLifetimes6(ClientContext6& ctx, uint32_t& preferred, uint32_t& valid) {
// If the triplets are specified in one of our classes use it.
}
}
- // If preferred isn't set or insane, calculate it as valid_lft * 0.625.
- if (!preferred || preferred > valid) {
- preferred = ((valid * 5)/8);
- LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
- ALLOC_ENGINE_V6_CALCULATED_PREFERRED_LIFETIME)
- .arg(ctx.query_->getLabel())
- .arg(preferred);
+ sanitizeLifetimes6(ctx, preferred, valid);
+}
+
+void
+AllocEngine::getMinLifetimes6(ClientContext6& ctx, uint32_t& preferred,
+ uint32_t& valid) {
+ // If the triplets are specified in one of our classes use it.
+ // We use the first one we find for each lifetime.
+ Triplet<uint32_t> candidate_preferred;
+ Triplet<uint32_t> candidate_valid;
+ const ClientClasses classes = ctx.query_->getClasses();
+ if (!classes.empty()) {
+ // Let's get class definitions
+ const ClientClassDictionaryPtr& dict =
+ CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
+
+ // Iterate over the assigned class definitions.
+ int have_both = 0;
+ for (auto const& name : classes) {
+ ClientClassDefPtr cl = dict->findClass(name);
+ if (candidate_preferred.unspecified() &&
+ (cl && (!cl->getPreferred().unspecified()))) {
+ candidate_preferred = cl->getPreferred();
+ ++have_both;
+ }
+
+ if (candidate_valid.unspecified() &&
+ (cl && (!cl->getValid().unspecified()))) {
+ candidate_valid = cl->getValid();
+ ++have_both;
+ }
+ if (have_both == 2) {
+ break;
+ }
+ }
+ }
+
+ // If no classes specified preferred lifetime, get it from the subnet.
+ if (!candidate_preferred) {
+ candidate_preferred = ctx.subnet_->getPreferred();
+ }
+
+ // If no classes specified valid lifetime, get it from the subnet.
+ if (!candidate_valid) {
+ candidate_valid = ctx.subnet_->getValid();
+ }
+
+ // Save remaining values.
+ uint32_t remain_preferred(preferred);
+ uint32_t remain_valid(valid);
+
+ // Set the outbound parameters to the minimal values.
+ preferred = candidate_preferred.getMin();
+ valid = candidate_valid.getMin();
+
+ // Return at least the remaining values.
+ if (remain_preferred > preferred) {
+ preferred = remain_preferred;
}
+ if (remain_valid > valid) {
+ valid = remain_valid;
+ }
+
+ sanitizeLifetimes6(ctx, preferred, valid);
}
Lease6Ptr AllocEngine::createLease6(ClientContext6& ctx,
return (candidate_lft.get());
}
+void
+AllocEngine::getMinValidLft(const ClientContext4& ctx, uint32_t& valid) {
+ // If it's BOOTP, use infinite valid lifetime.
+ if (ctx.query_->inClass("BOOTP")) {
+ valid = Lease::INFINITY_LFT;
+ return;
+ }
+
+ // If the triplet is specified in one of our classes use it.
+ // We use the first one we find.
+ Triplet<uint32_t> candidate_lft;
+ const ClientClasses classes = ctx.query_->getClasses();
+ if (!classes.empty()) {
+ // Let's get class definitions
+ const ClientClassDictionaryPtr& dict =
+ CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
+
+ // Iterate over the assigned class definitions.
+ for (auto const& name : classes) {
+ ClientClassDefPtr cl = dict->findClass(name);
+ if (cl && (!cl->getValid().unspecified())) {
+ candidate_lft = cl->getValid();
+ break;
+ }
+ }
+ }
+
+ // If no classes specified it, get it from the subnet.
+ if (!candidate_lft) {
+ candidate_lft = ctx.subnet_->getValid();
+ }
+
+ // Save remaining value.
+ uint32_t remain(valid);
+
+ // Set to the minimal value.
+ valid = candidate_lft.getMin();
+
+ // Return at least the remaining value.
+ if (remain > valid) {
+ valid = remain;
+ }
+}
+
Lease4Ptr
AllocEngine::createLease4(const ClientContext4& ctx, const IOAddress& addr,
CalloutHandle::CalloutNextStep& callout_status) {
/// @param [out] valid set to the valid lifetime that should be used.
static void getLifetimes6(ClientContext6& ctx, uint32_t& preferred,
uint32_t& valid);
+
+ /// @brief Determines the preferred and valid v6 lease lifetimes when
+ /// the pool occupancy is over the adaptive lease time threshold.
+ ///
+ /// As for the common case find the candidate triplet and return
+ /// minimal values. Requested lifetimes are ignored but remaining
+ /// lifetimes are returned when greater than minimal.
+ ///
+ /// @param ctx client context that passes all necessary information. See
+ /// @ref ClientContext6 for details.
+ /// @param [in/out] preferred set to the preferred lifetime that should
+ // be used. Caller must set it to 0 or remaining value.
+ /// @param [in/out] valid set to the valid lifetime that should be used.
+ /// Caller must set it to 0 or remaining value.
+ static void getMinLifetimes6(ClientContext6& ctx, uint32_t& preferred,
+ uint32_t& valid);
private:
/// @brief Creates a lease and inserts it in LeaseMgr if necessary
/// @return unsigned integer value of the valid lifetime to use.
static uint32_t getValidLft(const ClientContext4& ctx);
+ /// @brief Returns the valid lifetime based on the v4 context when
+ /// the pool occupancy is over the adaptive lease time threshold.
+ ///
+ /// If the client query is a BOOTP query, the value returned will
+ /// be Lease::INFINITY_LFT.
+ ///
+ /// Otherwise, as for the common case find the canndidate triplet
+ /// and return the minimal value. Requested lifetime is ignored but
+ /// remaining lifetime is returned when greater than minimal.
+ ///
+ /// @param ctx Client context holding various information about the client.
+ /// @param [in/out] valid set to the valid lifetime that should be used.
+ /// Caller must set it to 0 or remaining value.
+ /// @return unsigned integer value of the valid lifetime to use.
+ static void getMinValidLft(const ClientContext4& ctx, uint32_t& valid);
+
/// @brief Returns the offer lifetime based on the v4 context
///
/// If the client query is a BOOTP query or something other than
/// @return Collection of const @c Host objects.
ConstHostCollection
getAllInternal4(const SubnetID& subnet_id,
- const asiolink::IOAddress& address) const;
+ const asiolink::IOAddress& address) const;
/// @brief Returns @c Host objects for the specified (Subnet-id,IPv6 address) tuple.
///
/// @return A pointer to the unparsed configuration.
isc::data::ElementPtr
toElementWithMetadata(const bool include_metadata,
- CfgOptionDefPtr cfg_option_def = CfgOptionDefPtr()) const;
+ CfgOptionDefPtr cfg_option_def = CfgOptionDefPtr()) const;
private:
example: 0
- <b>fqdn_rev</b> - FQDN reverse DNS RR update flag \n
- type: bool (0 or 1) \n
+ type: bool (0 or 1) \n
example: 1
- <b>hostname</b> - hostname \n
}
}
-// Verifies that AllocEngine::getRemaining retuns the remaining lifetime value.
-TEST_F(AllocEngine4Test, getRemaining) {
- // No Lease.
- uint32_t valid(1);
- Lease4Ptr lease;
- AllocEngine::getRemaining(lease, valid);
- EXPECT_EQ(0, valid);
-
- // Unexpected state.
- valid = 1;
- uint8_t hwaddr_data[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe };
- HWAddrPtr hwaddr(new HWAddr(hwaddr_data, sizeof(hwaddr_data), HTYPE_ETHER));
- uint8_t clientid[] = { 8, 7, 6, 5, 4, 3, 2, 1 };
- time_t now = time(0);
- lease.reset(new Lease4(IOAddress("192.0.2.100"), hwaddr, clientid,
- sizeof(clientid), 100, now, 1));
- lease->state_ = Lease::STATE_DECLINED;
- AllocEngine::getRemaining(lease, valid);
- EXPECT_EQ(0, valid);
-
- // Infinite lifetime.
- lease->state_ = Lease::STATE_DEFAULT;
- uint32_t infinity_lft = Lease::INFINITY_LFT;
- lease->valid_lft_ = lease->current_valid_lft_ = infinity_lft;
- AllocEngine::getRemaining(lease, valid);
- EXPECT_EQ(infinity_lft, valid);
-
- // Time going backward.
- lease->cltt_ = lease->current_cltt_ = now + 100;
- lease->valid_lft_ = lease->current_valid_lft_ = 50;
- AllocEngine::getRemaining(lease, valid);
- EXPECT_EQ(0, valid);
-
- // Already expired.
- valid = 1;
- lease->cltt_ = lease->current_cltt_ = now - 100;
- AllocEngine::getRemaining(lease, valid);
- EXPECT_EQ(0, valid);
-
- // Valid case.
- now = time(0);
- lease->cltt_ = lease->current_cltt_ = now - 10;
- AllocEngine::getRemaining(lease, valid);
- EXPECT_NEAR(40, valid, 1);
-}
-
// Verifies that AllocEngine::getValidLft(ctx4) returns the appropriate
// lifetime value based on the context content.
TEST_F(AllocEngine4Test, getTemplateClassValidLft4) {
}
}
+// Verifies that AllocEngine::getMinValidLft(ctx4, valid) sets the appropriate
+// lifetime value based on the context content.
+TEST_F(AllocEngine4Test, getMinValidLft4) {
+ AllocEngine engine(0);
+
+ // Let's make three classes, two with valid-lifetime and one without,
+ // and add them to the dictionary.
+ ClientClassDictionaryPtr dictionary = CfgMgr::instance().getStagingCfg()->getClientClassDictionary();
+ ExpressionPtr match_expr;
+ ExpressionParser parser;
+
+ ElementPtr test_cfg = Element::create("'valid_one_value'");
+ parser.parse(match_expr, test_cfg, AF_INET, EvalContext::acceptAll, EvalContext::PARSER_STRING);
+
+ ClientClassDefPtr class_def(new TemplateClientClassDef("valid_one", match_expr));
+ Triplet<uint32_t> valid_one(50, 100, 150);
+ class_def->setValid(valid_one);
+ dictionary->addClass(class_def);
+
+ test_cfg = Element::create("'valid_two_value'");
+ parser.parse(match_expr, test_cfg, AF_INET, EvalContext::acceptAll, EvalContext::PARSER_STRING);
+
+ class_def.reset(new TemplateClientClassDef("valid_two", match_expr));
+ Triplet<uint32_t>valid_two(200, 250, 300);
+ class_def->setValid(valid_two);
+ dictionary->addClass(class_def);
+
+ test_cfg = Element::create("'valid_unspec_value'");
+ parser.parse(match_expr, test_cfg, AF_INET, EvalContext::acceptAll, EvalContext::PARSER_STRING);
+
+ class_def.reset(new TemplateClientClassDef("valid_unspec", match_expr));
+ dictionary->addClass(class_def);
+
+ // Commit our class changes.
+ CfgMgr::instance().commit();
+
+ // Update the subnet's triplet to something more useful.
+ subnet_->setValid(Triplet<uint32_t>(500, 1000, 1500));
+
+ // Describes a test scenario.
+ struct Scenario {
+ std::string desc_; // descriptive text for logging
+ std::vector<std::string> classes_; // class list of assigned classes
+ uint32_t requested_lft_; // use as option 51 is > 0
+ uint32_t remaining_lft_; // remaining lifime or 0
+ uint32_t exp_valid_; // expected lifetime
+ };
+
+ // Scenarios to test.
+ std::vector<Scenario> scenarios = {
+ {
+ "BOOTP",
+ { "BOOTP" },
+ 0,
+ 0,
+ Lease::INFINITY_LFT
+ },
+ {
+ "no classes, no option, remain 0",
+ {},
+ 0,
+ 0,
+ subnet_->getValid().getMin()
+ },
+ {
+ "no classes, no option, remain too small",
+ {},
+ 0,
+ 100,
+ subnet_->getValid().getMin()
+ },
+ {
+ "no classes, no option, remain",
+ {},
+ 0,
+ 800,
+ 800
+ },
+ {
+ "no classes, option, remain 0",
+ {},
+ 1000,
+ 0,
+ subnet_->getValid().getMin()
+ },
+ {
+ "class unspecified, no option, remain 0",
+ { "valid_unspec" },
+ 0,
+ 0,
+ subnet_->getValid().getMin()
+ },
+ {
+ "from last class, no option, remain 0",
+ { "valid_unspec", "valid_one" },
+ 0,
+ 0,
+ valid_one.getMin()
+ },
+ {
+ "from first class, no option, remain 0",
+ { "valid_two", "valid_one" },
+ 0,
+ 0,
+ valid_two.getMin()
+ },
+ {
+ "class plus remain too small",
+ { "valid_one" },
+ 0,
+ 10,
+ valid_one.getMin(),
+ },
+ {
+ "class plus remain",
+ { "valid_one" },
+ 0,
+ 100,
+ 100
+ }
+ };
+
+ // Iterate over the scenarios and verify the correct outcome.
+ for (auto const& scenario : scenarios) {
+ SCOPED_TRACE(scenario.desc_); {
+ // Create a context;
+ AllocEngine::ClientContext4 ctx(subnet_, ClientIdPtr(), hwaddr_,
+ IOAddress("0.0.0.0"), false, false,
+ "", false);
+ ctx.query_.reset(new Pkt4(DHCPDISCOVER, 1234));
+
+ // Add client classes (if any)
+ for (auto const& class_name : scenario.classes_) {
+ if (class_name == "BOOTP") {
+ ctx.query_->addClass(class_name);
+ } else {
+ string subclass(TemplateClientClassDef::SPAWN_CLASS_PREFIX);
+ subclass += class_name;
+ subclass += "_value";
+ ctx.query_->addSubClass(class_name, subclass);
+ }
+ }
+
+ // Add client option (if one)
+ if (scenario.requested_lft_) {
+ OptionUint32Ptr opt(new OptionUint32(Option::V4, DHO_DHCP_LEASE_TIME,
+ scenario.requested_lft_));
+ ctx.query_->addOption(opt);
+ }
+
+ uint32_t valid = scenario.remaining_lft_;
+ engine.getMinValidLft(ctx, valid);
+ EXPECT_EQ(valid, scenario.exp_valid_);
+ }
+ }
+}
+
+// Verifies that AllocEngine::getRemaining retuns the remaining lifetime value.
+TEST_F(AllocEngine4Test, getRemaining) {
+ // No Lease.
+ uint32_t valid(1);
+ Lease4Ptr lease;
+ AllocEngine::getRemaining(lease, valid);
+ EXPECT_EQ(0, valid);
+
+ // Unexpected state.
+ valid = 1;
+ uint8_t hwaddr_data[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe };
+ HWAddrPtr hwaddr(new HWAddr(hwaddr_data, sizeof(hwaddr_data), HTYPE_ETHER));
+ uint8_t clientid[] = { 8, 7, 6, 5, 4, 3, 2, 1 };
+ time_t now = time(0);
+ lease.reset(new Lease4(IOAddress("192.0.2.100"), hwaddr, clientid,
+ sizeof(clientid), 100, now, 1));
+ lease->state_ = Lease::STATE_DECLINED;
+ AllocEngine::getRemaining(lease, valid);
+ EXPECT_EQ(0, valid);
+
+ // Infinite lifetime.
+ lease->state_ = Lease::STATE_DEFAULT;
+ uint32_t infinity_lft = Lease::INFINITY_LFT;
+ lease->valid_lft_ = lease->current_valid_lft_ = infinity_lft;
+ AllocEngine::getRemaining(lease, valid);
+ EXPECT_EQ(infinity_lft, valid);
+
+ // Time going backward.
+ lease->cltt_ = lease->current_cltt_ = now + 100;
+ lease->valid_lft_ = lease->current_valid_lft_ = 50;
+ AllocEngine::getRemaining(lease, valid);
+ EXPECT_EQ(0, valid);
+
+ // Already expired.
+ valid = 1;
+ lease->cltt_ = lease->current_cltt_ = now - 100;
+ AllocEngine::getRemaining(lease, valid);
+ EXPECT_EQ(0, valid);
+
+ // Valid case.
+ now = time(0);
+ lease->cltt_ = lease->current_cltt_ = now - 10;
+ AllocEngine::getRemaining(lease, valid);
+ EXPECT_NEAR(40, valid, 1);
+}
+
// This test checks that deleteRelease handles BOOTP leases.
TEST_F(AllocEngine4Test, bootpDelete) {
boost::scoped_ptr<AllocEngine> engine;
}
}
-// Verifies that AllocEngine::getRemaining retuns the remaining lifetime values.
-TEST_F(AllocEngine6Test, getRemaining) {
- // No Lease.
- uint32_t valid(1);
- uint32_t preferred(1);
- Lease6Ptr lease;
- AllocEngine::getRemaining(lease, valid, preferred);
- EXPECT_EQ(0, valid);
- EXPECT_EQ(0, preferred);
-
- // Unexpected state.
- valid = 1;
- preferred = 1;
- DuidPtr duid(new DUID(vector<uint8_t>(12, 0xff)));
- const uint32_t iaid = 3568;
- time_t now = time(0);
- lease.reset(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"), duid,
- iaid, 30, 50, 1));
- lease->state_ = Lease::STATE_DECLINED;
- AllocEngine::getRemaining(lease, valid, preferred);
- EXPECT_EQ(0, valid);
- EXPECT_EQ(0, preferred);
-
- // Time going backward.
- valid = 1;
- preferred = 1;
- lease->state_ = Lease::STATE_DEFAULT;
- lease->cltt_ = lease->current_cltt_ = now + 100;
- lease->valid_lft_ = lease->current_valid_lft_ = 50;
- AllocEngine::getRemaining(lease, valid, preferred);
- EXPECT_EQ(0, valid);
- EXPECT_EQ(0, preferred);
-
- // Already expired.
- valid = 1;
- preferred = 1;
- lease->cltt_ = lease->current_cltt_ = now - 100;
- AllocEngine::getRemaining(lease, valid, preferred);
- EXPECT_EQ(0, valid);
- EXPECT_EQ(0, preferred);
-
- // Valid case.
- now = time(0);
- lease->cltt_ = lease->current_cltt_ = now - 10;
- AllocEngine::getRemaining(lease, valid, preferred);
- EXPECT_NEAR(40, valid, 1);
- EXPECT_NEAR(20, preferred, 1);
-
- // No longer preferred.
- now = time(0);
- lease->cltt_ = lease->current_cltt_ = now - 40;
- AllocEngine::getRemaining(lease, valid, preferred);
- EXPECT_NEAR(10, valid, 1);
- EXPECT_EQ(0, preferred);
-}
-
// Verifies that AllocEngine::getLifetimes6() returns the appropriate
// preferred lifetime value based on the context content.
TEST_F(AllocEngine6Test, getPreferredLifetime) {
}
}
+// Verifies that AllocEngine::getMinLifetimes6() returns the appropriate
+// valid lifetime value based on the context content.
+TEST_F(AllocEngine6Test, getMinValidLifetime) {
+ boost::scoped_ptr<AllocEngine> engine;
+ ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
+ ASSERT_TRUE(engine);
+
+ // Let's make three classes, two with valid-lifetime and one without,
+ // and add them to the dictionary.
+ ClientClassDictionaryPtr dictionary = CfgMgr::instance().getStagingCfg()->getClientClassDictionary();
+ ExpressionPtr match_expr;
+ ExpressionParser parser;
+
+ ElementPtr test_cfg = Element::create("'valid_one_value'");
+ parser.parse(match_expr, test_cfg, AF_INET6, EvalContext::acceptAll, EvalContext::PARSER_STRING);
+
+ ClientClassDefPtr class_def(new TemplateClientClassDef("valid_one", match_expr));
+ Triplet<uint32_t> valid_one(50, 100, 150);
+ class_def->setValid(valid_one);
+ dictionary->addClass(class_def);
+
+ test_cfg = Element::create("'valid_two_value'");
+ parser.parse(match_expr, test_cfg, AF_INET6, EvalContext::acceptAll, EvalContext::PARSER_STRING);
+
+ class_def.reset(new TemplateClientClassDef("valid_two", match_expr));
+ Triplet<uint32_t>valid_two(200, 250, 300);
+ class_def->setValid(valid_two);
+ dictionary->addClass(class_def);
+
+ test_cfg = Element::create("'valid_unspec_value'");
+ parser.parse(match_expr, test_cfg, AF_INET6, EvalContext::acceptAll, EvalContext::PARSER_STRING);
+
+ class_def.reset(new TemplateClientClassDef("valid_unspec", match_expr));
+ dictionary->addClass(class_def);
+
+ // Commit our class changes.
+ CfgMgr::instance().commit();
+
+ // Update the subnet's triplet to something more useful.
+ subnet_->setValid(Triplet<uint32_t>(500, 1000, 1500));
+
+ // Describes a test scenario.
+ struct Scenario {
+ std::string desc_; // descriptive text for logging
+ std::vector<std::string> classes_; // class list of assigned classes
+ uint32_t requested_lft_; // use as option 51 is > 0
+ uint32_t remaining_lft_; // remaining valid lifetime
+ uint32_t exp_valid_; // expected lifetime
+ };
+
+ // Scenarios to test.
+ std::vector<Scenario> scenarios = {
+ {
+ "no classes, no hint, remain 0",
+ {},
+ 0,
+ 0,
+ subnet_->getValid().getMin()
+ },
+ {
+ "no classes, no hint, remain too small",
+ {},
+ 0,
+ 100,
+ subnet_->getValid().getMin()
+ },
+ {
+ "no classes, no hint, remain",
+ {},
+ 0,
+ 800,
+ 800
+ },
+ {
+ "no classes, hint, remain 0",
+ {},
+ 800,
+ 0,
+ subnet_->getValid().getMin()
+ },
+ {
+ "class unspecified, no hint, remain 0",
+ { "valid_unspec" },
+ 0,
+ 0,
+ subnet_->getValid().getMin()
+ },
+ {
+ "from last class, no hint, remain 0",
+ { "valid_unspec", "valid_one" },
+ 0,
+ 0,
+ valid_one.getMin()
+ },
+ {
+ "from first class, no hint, remain 0",
+ { "valid_two", "valid_one" },
+ 0,
+ 0,
+ valid_two.getMin()
+ },
+ {
+ "class plus remain too small",
+ { "valid_one" },
+ 0,
+ 10,
+ valid_one.getMin()
+ },
+ {
+ "class plus remain",
+ { "valid_one" },
+ 0,
+ 100,
+ 100
+ }
+ };
+
+ // Iterate over the scenarios and verify the correct outcome.
+ for (auto const& scenario : scenarios) {
+ SCOPED_TRACE(scenario.desc_); {
+ // Create a context;
+ AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", true,
+ Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)));
+ // Add client classes (if any)
+ for (auto const& class_name : scenario.classes_) {
+ ctx.query_->addClass(class_name);
+ }
+
+ // Add hint
+ ctx.currentIA().iaid_ = iaid_;
+
+ // prefix, prefixlen, preferred, valid
+ ctx.currentIA().addHint(IOAddress("::"), 128, 0, scenario.requested_lft_);
+ uint32_t valid = scenario.remaining_lft_;
+ uint32_t preferred = 0;
+
+ engine->getMinLifetimes6(ctx, preferred, valid);
+ EXPECT_EQ(valid, scenario.exp_valid_);
+ }
+ }
+}
+
+// Verifies that AllocEngine::getMinLifetimes6() returns the appropriate
+// preferred lifetime value based on the context content.
+TEST_F(AllocEngine6Test, getMinPreferredLifetime) {
+ boost::scoped_ptr<AllocEngine> engine;
+ ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
+ ASSERT_TRUE(engine);
+
+ // Let's make three classes, two with preferred-lifetime and one without,
+ // and add them to the dictionary.
+ ClientClassDictionaryPtr dictionary = CfgMgr::instance().getStagingCfg()->getClientClassDictionary();
+ ExpressionPtr match_expr;
+ ExpressionParser parser;
+
+ ElementPtr test_cfg = Element::create("'preferred_one_value'");
+ parser.parse(match_expr, test_cfg, AF_INET6, EvalContext::acceptAll, EvalContext::PARSER_STRING);
+
+ ClientClassDefPtr class_def(new TemplateClientClassDef("preferred_one", match_expr));
+ Triplet<uint32_t> preferred_one(50, 100, 150);
+ class_def->setPreferred(preferred_one);
+ dictionary->addClass(class_def);
+
+ test_cfg = Element::create("'preferred_two_value'");
+ parser.parse(match_expr, test_cfg, AF_INET6, EvalContext::acceptAll, EvalContext::PARSER_STRING);
+
+ class_def.reset(new TemplateClientClassDef("preferred_two", match_expr));
+ Triplet<uint32_t>preferred_two(200, 250, 300);
+ class_def->setPreferred(preferred_two);
+ dictionary->addClass(class_def);
+
+ test_cfg = Element::create("'preferred_unspec_value'");
+ parser.parse(match_expr, test_cfg, AF_INET6, EvalContext::acceptAll, EvalContext::PARSER_STRING);
+
+ class_def.reset(new TemplateClientClassDef("preferred_unspec", match_expr));
+ dictionary->addClass(class_def);
+
+ // Commit our class changes.
+ CfgMgr::instance().commit();
+
+ // Update the subnet's triplet to something more useful. Note that
+ // valid is 400 for the subnet.
+ subnet_->setPreferred(Triplet<uint32_t>(300, 350, 450));
+
+ // Describes a test scenario.
+ struct Scenario {
+ std::string desc_; // descriptive text for logging
+ std::vector<std::string> classes_; // class list of assigned classes
+ uint32_t requested_lft_; // use as option 51 is > 0
+ uint32_t remaining_lft_; // remaining preferred lifetime
+ uint32_t exp_preferred_; // expected lifetime
+ };
+
+ // Scenarios to test.
+ std::vector<Scenario> scenarios = {
+ {
+ "no classes, no hint, remain 0",
+ {},
+ 0,
+ 0,
+ subnet_->getPreferred().getMin()
+ },
+ {
+ "no classes, no hint, remain too small",
+ {},
+ 0,
+ 100,
+ subnet_->getPreferred().getMin()
+ },
+ {
+ "no classes, no hint, remain",
+ {},
+ 0,
+ 350,
+ 350
+ },
+ {
+ "no classes, no hint, remain too big",
+ {},
+ 0,
+ 500,
+ subnet_->getValid().getMin() * 5 / 8
+ },
+ {
+ "no classes, hint, remain 0",
+ {},
+ 800,
+ 0,
+ subnet_->getPreferred().getMin()
+ },
+ {
+ "class unspecified, no hint, remain 0",
+ { "preferred_unspec" },
+ 0,
+ 0,
+ subnet_->getPreferred().getMin()
+ },
+ {
+ "from last class, no hint, remain 0",
+ { "preferred_unspec", "preferred_one" },
+ 0,
+ 0,
+ preferred_one.getMin()
+ },
+ {
+ "from first class, no hint, remain 0",
+ { "preferred_two", "preferred_one" },
+ 0,
+ 0,
+ preferred_two.getMin()
+ },
+ {
+ "class plus remain too small",
+ { "preferred_one" },
+ 0,
+ 10,
+ preferred_one.getMin()
+ },
+ {
+ "class plus remain",
+ { "preferred_one" },
+ 0,
+ 100,
+ 100
+ }
+ };
+
+ // Iterate over the scenarios and verify the correct outcome.
+ for (auto const& scenario : scenarios) {
+ SCOPED_TRACE(scenario.desc_); {
+ // Create a context;
+ AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", true,
+ Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)));
+ // Add client classes (if any)
+ for (auto const& class_name : scenario.classes_) {
+ string subclass(TemplateClientClassDef::SPAWN_CLASS_PREFIX);
+ subclass += class_name;
+ subclass += "_value";
+ ctx.query_->addSubClass(class_name, subclass);
+ }
+
+ // Add hint
+ ctx.currentIA().iaid_ = iaid_;
+
+ // prefix, prefixlen, preferred, valid
+ ctx.currentIA().addHint(IOAddress("::"), 128, scenario.requested_lft_, 0);
+
+ uint32_t valid = 0;
+ uint32_t preferred = scenario.remaining_lft_;
+
+ engine->getMinLifetimes6(ctx, preferred, valid);
+ EXPECT_EQ(preferred, scenario.exp_preferred_);
+ }
+ }
+}
+
+// Verifies that AllocEngine::getRemaining retuns the remaining lifetime values.
+TEST_F(AllocEngine6Test, getRemaining) {
+ // No Lease.
+ uint32_t valid(1);
+ uint32_t preferred(1);
+ Lease6Ptr lease;
+ AllocEngine::getRemaining(lease, valid, preferred);
+ EXPECT_EQ(0, valid);
+ EXPECT_EQ(0, preferred);
+
+ // Unexpected state.
+ valid = 1;
+ preferred = 1;
+ DuidPtr duid(new DUID(vector<uint8_t>(12, 0xff)));
+ const uint32_t iaid = 3568;
+ time_t now = time(0);
+ lease.reset(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"), duid,
+ iaid, 30, 50, 1));
+ lease->state_ = Lease::STATE_DECLINED;
+ AllocEngine::getRemaining(lease, valid, preferred);
+ EXPECT_EQ(0, valid);
+ EXPECT_EQ(0, preferred);
+
+ // Time going backward.
+ valid = 1;
+ preferred = 1;
+ lease->state_ = Lease::STATE_DEFAULT;
+ lease->cltt_ = lease->current_cltt_ = now + 100;
+ lease->valid_lft_ = lease->current_valid_lft_ = 50;
+ AllocEngine::getRemaining(lease, valid, preferred);
+ EXPECT_EQ(0, valid);
+ EXPECT_EQ(0, preferred);
+
+ // Already expired.
+ valid = 1;
+ preferred = 1;
+ lease->cltt_ = lease->current_cltt_ = now - 100;
+ AllocEngine::getRemaining(lease, valid, preferred);
+ EXPECT_EQ(0, valid);
+ EXPECT_EQ(0, preferred);
+
+ // Valid case.
+ now = time(0);
+ lease->cltt_ = lease->current_cltt_ = now - 10;
+ AllocEngine::getRemaining(lease, valid, preferred);
+ EXPECT_NEAR(40, valid, 1);
+ EXPECT_NEAR(20, preferred, 1);
+
+ // No longer preferred.
+ now = time(0);
+ lease->cltt_ = lease->current_cltt_ = now - 40;
+ AllocEngine::getRemaining(lease, valid, preferred);
+ EXPECT_NEAR(10, valid, 1);
+ EXPECT_EQ(0, preferred);
+}
+
} // namespace test
} // namespace dhcp
} // namespace isc
// Verifies valid permutations of ddns-ttl-percent, ddns-ttl,
// ddns-ttl-min, and ddns-ttl-max values for SubnetX.
template<typename ParserType, typename NetworkPtrType>
- void validDdnsTtlParmatersSubnet(int family) {
- struct Scenario {
- size_t line_no_;
- std::string json_;
- double ddns_ttl_percent_;
- uint32_t ddns_ttl_;
- uint32_t ddns_ttl_min_;
- uint32_t ddns_ttl_max_;
- };
-
- std::list<Scenario> scenarios = {
- {
- __LINE__,
- R"^({
- "id": 1,
- "ddns-ttl": 100
- })^",
- 0.0, 100, 0, 0
- },{
- __LINE__,
- R"^({
- "id": 1,
- "ddns-ttl-percent": 5.0
- })^",
- 5.0, 0, 0, 0
- },{
- __LINE__,
- R"^({
- "id": 1,
- "ddns-ttl-min": 25
- })^",
- 0.0, 0, 25, 0
- },{
- __LINE__,
- R"^({
- "id": 1,
- "ddns-ttl-max": 150
- })^",
- 0.0, 0, 0, 150
- },{
- __LINE__,
- R"^({
- "id": 1,
- "ddns-ttl-min": 25,
- "ddns-ttl-max": 150
- })^",
- 0.0, 0, 25, 150
- },{
- __LINE__,
- R"^({
- "id": 1, "subnet": "192.0.2.0/24",
- "ddns-ttl-percent": 5.0,
- "ddns-ttl-min": 25,
- "ddns-ttl-max": 150
- })^",
- 5.0, 0, 25, 150
- }};
+ void validDdnsTtlParmatersSubnet(int family) {
+ struct Scenario {
+ size_t line_no_;
+ std::string json_;
+ double ddns_ttl_percent_;
+ uint32_t ddns_ttl_;
+ uint32_t ddns_ttl_min_;
+ uint32_t ddns_ttl_max_;
+ };
+
+ std::list<Scenario> scenarios = {
+ {
+ __LINE__,
+ R"^({
+ "id": 1,
+ "ddns-ttl": 100
+ })^",
+ 0.0, 100, 0, 0
+ },{
+ __LINE__,
+ R"^({
+ "id": 1,
+ "ddns-ttl-percent": 5.0
+ })^",
+ 5.0, 0, 0, 0
+ },{
+ __LINE__,
+ R"^({
+ "id": 1,
+ "ddns-ttl-min": 25
+ })^",
+ 0.0, 0, 25, 0
+ },{
+ __LINE__,
+ R"^({
+ "id": 1,
+ "ddns-ttl-max": 150
+ })^",
+ 0.0, 0, 0, 150
+ },{
+ __LINE__,
+ R"^({
+ "id": 1,
+ "ddns-ttl-min": 25,
+ "ddns-ttl-max": 150
+ })^",
+ 0.0, 0, 25, 150
+ },{
+ __LINE__,
+ R"^({
+ "id": 1, "subnet": "192.0.2.0/24",
+ "ddns-ttl-percent": 5.0,
+ "ddns-ttl-min": 25,
+ "ddns-ttl-max": 150
+ })^",
+ 5.0, 0, 25, 150
+ }};
ElementPtr subnet_elem = Element::create(family == AF_INET ?
"192.0.2.0/24" : "2001:db8::/64");
- for (const auto& scenario : scenarios) {
- std::stringstream oss;
- oss << "scenario at " << scenario.line_no_;
- SCOPED_TRACE(oss.str());
-
- // Parse configuration specified above.
- ElementPtr config_element;
- ASSERT_NO_THROW_LOG(config_element = Element::fromJSON(scenario.json_));
+ for (const auto& scenario : scenarios) {
+ std::stringstream oss;
+ oss << "scenario at " << scenario.line_no_;
+ SCOPED_TRACE(oss.str());
+
+ // Parse configuration specified above.
+ ElementPtr config_element;
+ ASSERT_NO_THROW_LOG(config_element = Element::fromJSON(scenario.json_));
config_element->set("subnet", subnet_elem);
- ParserType parser(family);
+ ParserType parser(family);
- NetworkPtrType subnet;
+ NetworkPtrType subnet;
- ASSERT_NO_THROW_LOG(subnet = parser.parse(config_element));
- ASSERT_TRUE(subnet);
+ ASSERT_NO_THROW_LOG(subnet = parser.parse(config_element));
+ ASSERT_TRUE(subnet);
- EXPECT_EQ(subnet->getDdnsTtlPercent().unspecified(), (scenario.ddns_ttl_percent_ == 0.0));
- EXPECT_EQ(subnet->getDdnsTtlPercent(), scenario.ddns_ttl_percent_);
+ EXPECT_EQ(subnet->getDdnsTtlPercent().unspecified(), (scenario.ddns_ttl_percent_ == 0.0));
+ EXPECT_EQ(subnet->getDdnsTtlPercent(), scenario.ddns_ttl_percent_);
- EXPECT_EQ(subnet->getDdnsTtl().unspecified(), (scenario.ddns_ttl_ == 0));
- EXPECT_EQ(subnet->getDdnsTtl(), scenario.ddns_ttl_);
+ EXPECT_EQ(subnet->getDdnsTtl().unspecified(), (scenario.ddns_ttl_ == 0));
+ EXPECT_EQ(subnet->getDdnsTtl(), scenario.ddns_ttl_);
- EXPECT_EQ(subnet->getDdnsTtlMin().unspecified(), (scenario.ddns_ttl_min_ == 0));
- EXPECT_EQ(subnet->getDdnsTtlMin(), scenario.ddns_ttl_min_);
+ EXPECT_EQ(subnet->getDdnsTtlMin().unspecified(), (scenario.ddns_ttl_min_ == 0));
+ EXPECT_EQ(subnet->getDdnsTtlMin(), scenario.ddns_ttl_min_);
- EXPECT_EQ(subnet->getDdnsTtlMax().unspecified(), (scenario.ddns_ttl_max_ == 0));
- EXPECT_EQ(subnet->getDdnsTtlMax(), scenario.ddns_ttl_max_);
- }
- }
+ EXPECT_EQ(subnet->getDdnsTtlMax().unspecified(), (scenario.ddns_ttl_max_ == 0));
+ EXPECT_EQ(subnet->getDdnsTtlMax(), scenario.ddns_ttl_max_);
+ }
+ }
- // Verifies invalid permutations of ddns-ttl-percent, ddns-ttl,
- // ddns-ttl-min, and ddns-ttl-max values for SubnetX.
+ // Verifies invalid permutations of ddns-ttl-percent, ddns-ttl,
+ // ddns-ttl-min, and ddns-ttl-max values for SubnetX.
template<typename ParserType>
- void invalidDdnsTtlParmatersSubnet(int family) {
- struct Scenario {
- size_t line_no_;
- std::string json_;
- std::string exp_message_;
- };
-
- std::list<Scenario> scenarios = {
- {
- __LINE__,
- R"^({
- "id": 1,
- "ddns-ttl-percent": 5.0,
- "ddns-ttl": 100
- })^",
- "subnet configuration failed: cannot specify both ddns-ttl-percent and ddns-ttl"
- },{
- __LINE__,
- R"^({
- "id": 1,
- "ddns-ttl": 100,
- "ddns-ttl-min": 25
- })^",
- "subnet configuration failed: cannot specify both ddns-ttl-min and ddns-ttl"
- },{
- __LINE__,
- R"^({
- "id": 1,
- "ddns-ttl": 100,
- "ddns-ttl-max": 150
- })^",
- "subnet configuration failed: cannot specify both ddns-ttl-max and ddns-ttl"
- },{
- __LINE__,
- R"^({
- "id": 1,
- "ddns-ttl-min": 150,
- "ddns-ttl-max": 25
- })^",
- "subnet configuration failed: ddns-ttl-max: 25 must be greater than ddns-ttl-min: 150"
- }};
+ void invalidDdnsTtlParmatersSubnet(int family) {
+ struct Scenario {
+ size_t line_no_;
+ std::string json_;
+ std::string exp_message_;
+ };
+
+ std::list<Scenario> scenarios = {
+ {
+ __LINE__,
+ R"^({
+ "id": 1,
+ "ddns-ttl-percent": 5.0,
+ "ddns-ttl": 100
+ })^",
+ "subnet configuration failed: cannot specify both ddns-ttl-percent and ddns-ttl"
+ },{
+ __LINE__,
+ R"^({
+ "id": 1,
+ "ddns-ttl": 100,
+ "ddns-ttl-min": 25
+ })^",
+ "subnet configuration failed: cannot specify both ddns-ttl-min and ddns-ttl"
+ },{
+ __LINE__,
+ R"^({
+ "id": 1,
+ "ddns-ttl": 100,
+ "ddns-ttl-max": 150
+ })^",
+ "subnet configuration failed: cannot specify both ddns-ttl-max and ddns-ttl"
+ },{
+ __LINE__,
+ R"^({
+ "id": 1,
+ "ddns-ttl-min": 150,
+ "ddns-ttl-max": 25
+ })^",
+ "subnet configuration failed: ddns-ttl-max: 25 must be greater than ddns-ttl-min: 150"
+ }};
ElementPtr subnet_elem = Element::create(family == AF_INET ?
"192.0.2.0/24" : "2001:db8::/64");
- for (const auto& scenario : scenarios) {
- std::stringstream oss;
- oss << "scenario at " << scenario.line_no_;
- SCOPED_TRACE(oss.str());
-
- // Parse configuration specified above.
- ElementPtr config_element;
- ASSERT_NO_THROW_LOG(config_element = Element::fromJSON(scenario.json_));
+ for (const auto& scenario : scenarios) {
+ std::stringstream oss;
+ oss << "scenario at " << scenario.line_no_;
+ SCOPED_TRACE(oss.str());
+
+ // Parse configuration specified above.
+ ElementPtr config_element;
+ ASSERT_NO_THROW_LOG(config_element = Element::fromJSON(scenario.json_));
config_element->set("subnet", subnet_elem);
- ParserType parser(family);
- ASSERT_THROW_MSG(parser.parse(config_element), DhcpConfigError, scenario.exp_message_);
- }
- }
+ ParserType parser(family);
+ ASSERT_THROW_MSG(parser.parse(config_element), DhcpConfigError, scenario.exp_message_);
+ }
+ }
/// @brief Tests valid DDNS parameters in v4 or v6 pools.
///
/// @param pool1 string pool specification for the first pool
/// @param pool2 string pool specification for the first pool
template<typename PoolListParserType>
- void validPoolDdnsParameters(const std::string& pool1, const std::string& pool2) {
+ void validPoolDdnsParameters(const std::string& pool1, const std::string& pool2) {
std::stringstream ss;
ss <<
/// @brief Sets the Hooks path from which hooks can be loaded.
/// @param custom_path path to use as the hooks path.
void setHooksTestPath(const std::string explicit_path = "") {
- HooksLibrariesParser::getHooksPath(true,
- (!explicit_path.empty() ?
+ HooksLibrariesParser::getHooksPath(true,
+ (!explicit_path.empty() ?
explicit_path : DHCPSRV_HOOKS_TEST_PATH));
}
// Verifies valid DDNS parameters in v4 pools.
TEST_F(DhcpParserTest, validDdnsParmatersPool4) {
- validPoolDdnsParameters<Pools4ListParser>("192.0.1.0/24", "192.0.2.0/24");
+ validPoolDdnsParameters<Pools4ListParser>("192.0.1.0/24", "192.0.2.0/24");
}
// Verifies valid DDNS parameters in v6 pools.
TEST_F(DhcpParserTest, validDdnsParmatersPool6) {
- validPoolDdnsParameters<Pools6ListParser>("2001:db8:1::/64", "2001:db8:2::/64");
+ validPoolDdnsParameters<Pools6ListParser>("2001:db8:1::/64", "2001:db8:2::/64");
}
} // Anonymous namespace