--- /dev/null
+// Copyright (C) 2015 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
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cc/data.h>
+#include <dhcpsrv/cfg_expiration.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/parsers/expiration_config_parser.h>
+#include <boost/foreach.hpp>
+
+using namespace isc::data;
+
+namespace isc {
+namespace dhcp {
+
+ExpirationConfigParser::ExpirationConfigParser()
+ : DhcpConfigParser() {
+}
+
+void
+ExpirationConfigParser::build(ConstElementPtr expiration_config) {
+ CfgExpirationPtr cfg = CfgMgr::instance().getStagingCfg()->getCfgExpiration();
+
+ BOOST_FOREACH(ConfigPair config_element, expiration_config->mapValue()) {
+
+ // Get parameter name and value.
+ std::string param_name = config_element.first;
+ ConstElementPtr param_value = config_element.second;
+
+ try {
+ // Set configuration parameters.
+ if (param_name == "reclaim-timer-wait-time") {
+ cfg->setReclaimTimerWaitTime(param_value->intValue());
+
+ } else if (param_name == "flush-reclaimed-timer-wait-time") {
+ cfg->setFlushReclaimedTimerWaitTime(param_value->intValue());
+
+ } else if (param_name == "hold-reclaimed-time") {
+ cfg->setHoldReclaimedTime(param_value->intValue());
+
+ } else if (param_name == "max-reclaim-leases") {
+ cfg->setMaxReclaimLeases(param_value->intValue());
+
+ } else if (param_name == "max-reclaim-time") {
+ cfg->setMaxReclaimTime(param_value->intValue());
+
+ } else if (param_name == "unwarned-reclaim-cycles") {
+ cfg->setUnwarnedReclaimCycles(param_value->intValue());
+
+ } else {
+ isc_throw(DhcpConfigError, "unsupported parameter '"
+ << param_name << "'");
+ }
+
+ } catch (const std::exception& ex) {
+ // Append position of the configuration parameter to the error
+ // message.
+ isc_throw(DhcpConfigError, ex.what() << " ("
+ << param_value->getPosition() << ")");
+ }
+ }
+}
+
+void
+ExpirationConfigParser::commit() {
+ // Nothing to do.
+}
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
--- /dev/null
+// Copyright (C) 2015 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
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef EXPIRATION_CONFIG_PARSER_H
+#define EXPIRATION_CONFIG_PARSER_H
+
+#include <dhcpsrv/parsers/dhcp_config_parser.h>
+
+namespace isc {
+namespace dhcp {
+
+
+/// @brief Parser for the configuration parameters pertaining to the
+/// processing of expired leases.
+///
+/// This parser iterates over parameters stored in the map and tries to
+/// set the appropriate values in the @c CfgExpiration object of the
+/// Configuration Manager.
+///
+/// Currently supported parameters are:
+/// - reclaim-timer-wait-time,
+/// - flush-reclaimed-timer-wait-time,
+/// - hold-reclaimed-time,
+/// - max-reclaim-leases,
+/// - max-reclaim-time,
+/// - unwarned-reclaim-cycles.
+///
+/// These parameters are optional and the default values are used for
+/// those that aren't specified.
+///
+/// The parser checks if the values of the specified parameters are within
+/// the allowed ranges and throws exception if they are. Each parameter
+/// has a corresponding maximum value defined in the @c CfgExpiration class.
+/// None of them may be negative.
+class ExpirationConfigParser : public DhcpConfigParser {
+public:
+
+ /// @brief Constructor
+ ExpirationConfigParser();
+
+ /// @brief Parses parameters in the JSON map, pertaining to the processing
+ /// of the expired leases.
+ ///
+ /// @param value pointer to the content of parsed values
+ ///
+ /// @throw DhcpConfigError if unknown parameter specified or the
+ /// parameter contains invalid value..
+ virtual void build(isc::data::ConstElementPtr value);
+
+ /// @brief Does nothing.
+ virtual void commit();
+
+};
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
+
+#endif // EXPIRATION_CONFIG_PARSER_H
--- /dev/null
+// Copyright (C) 2015 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
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <cc/data.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/cfg_expiration.h>
+#include <dhcpsrv/parsers/expiration_config_parser.h>
+#include <gtest/gtest.h>
+#include <sstream>
+#include <stdint.h>
+#include <string>
+
+using namespace isc::data;
+using namespace isc::dhcp;
+
+namespace {
+
+/// @brief Test fixture class for @c ExpirationConfigParser.
+class ExpirationConfigParserTest : public ::testing::Test {
+protected:
+
+ /// @brief Setup for each test.
+ ///
+ /// Clears the configuration in the @c CfgMgr.
+ virtual void SetUp();
+
+ /// @brief Cleans up after each test.
+ ///
+ /// Clears the configuration in the @c CfgMgr.
+ virtual void TearDown();
+
+ /// @brief Include a specified parameter in the configuration.
+ ///
+ /// If the specified parameter already exists, its value is replaced.
+ ///
+ /// @param param_name Parameter name.
+ /// @param value Parameter value.
+ void addParam(const std::string& param_name, const int64_t value);
+
+ /// @brief Creates configuration and parses it with the parser under test.
+ ///
+ /// This method creates the JSON configuration form the parameters
+ /// specified using the @c ExpirationConfigParserTest::addParam method.
+ /// It then uses the parser to parse this configuration. Any exceptions
+ /// emitted by the parser are propagated to the caller (they aren't
+ /// caught by this method).
+ ///
+ /// @return Pointer to the parsed configuration.
+ CfgExpirationPtr renderConfig() const;
+
+ /// @brief Tests that the out of range parameter value is not accepted.
+ ///
+ /// This test checks that the negative value and the value which is
+ /// greater than the maximum for the given parameter is not accepted.
+ ///
+ /// @param param Parameter name.
+ /// @param max_value Maximum value allowed for the parameter.
+ void testOutOfRange(const std::string& param, const uint64_t max_value);
+
+private:
+
+ /// @brief Holds configuration parameters specified for a test.
+ std::map<std::string, int64_t> config_params_;
+
+};
+
+void
+ExpirationConfigParserTest::SetUp() {
+ CfgMgr::instance().clear();
+}
+
+void
+ExpirationConfigParserTest::TearDown() {
+ CfgMgr::instance().clear();
+}
+
+void
+ExpirationConfigParserTest::addParam(const std::string& param_name,
+ const int64_t value) {
+ config_params_[param_name] = value;
+}
+
+CfgExpirationPtr
+ExpirationConfigParserTest::renderConfig() const {
+ std::ostringstream s;
+ // Create JSON configuration from the parameters in the map.
+ s << "{";
+ for (std::map<std::string, int64_t>::const_iterator param =
+ config_params_.begin(); param != config_params_.end();
+ ++param) {
+ // Include comma sign if we're at the subsequent parameter.
+ if (std::distance(config_params_.begin(), param) > 0) {
+ s << ",";
+ }
+ s << "\"" << param->first << "\": " << param->second;
+ }
+ s << "}";
+
+ ElementPtr config_element = Element::fromJSON(s.str());
+
+ // Parse the configuration. This may emit exceptions.
+ ExpirationConfigParser parser;
+ parser.build(config_element);
+
+ // No exception so return configuration.
+ return (CfgMgr::instance().getStagingCfg()->getCfgExpiration());
+}
+
+void
+ExpirationConfigParserTest::testOutOfRange(const std::string& param,
+ const uint64_t max_value) {
+ // Remove any existing parameters which would influence the
+ // behavior of the test.
+ config_params_.clear();
+
+ // Negative value is not allowed.
+ addParam(param, -3);
+ EXPECT_THROW(renderConfig(), DhcpConfigError)
+ << "test for negative value of '" << param << "' failed";
+
+ // Value greater than maximum is not allowed.
+ addParam(param, max_value + 1);
+ EXPECT_THROW(renderConfig(), DhcpConfigError)
+ << "test for out of range value of '" << param << "' failed";
+
+ // Value in range should be accepted.
+ addParam(param, max_value);
+ EXPECT_NO_THROW(renderConfig())
+ << "test for in range value of '" << param << "' failed";
+}
+
+
+// This test verifies that all parameters for the expiration may be configured.
+TEST_F(ExpirationConfigParserTest, allParameters) {
+ // Create configuration which overrides default values of all parameters.
+ addParam("reclaim-timer-wait-time", 20);
+ addParam("flush-reclaimed-timer-wait-time", 35);
+ addParam("hold-reclaimed-time", 1800);
+ addParam("max-reclaim-leases", 50);
+ addParam("max-reclaim-time", 100);
+ addParam("unwarned-reclaim-cycles", 10);
+
+ CfgExpirationPtr cfg;
+ ASSERT_NO_THROW(cfg = renderConfig());
+ EXPECT_EQ(20, cfg->getReclaimTimerWaitTime());
+ EXPECT_EQ(35, cfg->getFlushReclaimedTimerWaitTime());
+ EXPECT_EQ(1800, cfg->getHoldReclaimedTime());
+ EXPECT_EQ(50, cfg->getMaxReclaimLeases());
+ EXPECT_EQ(100, cfg->getMaxReclaimTime());
+ EXPECT_EQ(10, cfg->getUnwarnedReclaimCycles());
+}
+
+// This test verifies that default values are used if no parameter is
+// specified.
+TEST_F(ExpirationConfigParserTest, noParameters) {
+ CfgExpirationPtr cfg;
+ ASSERT_NO_THROW(cfg = renderConfig());
+ EXPECT_EQ(CfgExpiration::DEFAULT_RECLAIM_TIMER_WAIT_TIME,
+ cfg->getReclaimTimerWaitTime());
+ EXPECT_EQ(CfgExpiration::DEFAULT_FLUSH_RECLAIMED_TIMER_WAIT_TIME,
+ cfg->getFlushReclaimedTimerWaitTime());
+ EXPECT_EQ(CfgExpiration::DEFAULT_HOLD_RECLAIMED_TIME,
+ cfg->getHoldReclaimedTime());
+ EXPECT_EQ(CfgExpiration::DEFAULT_MAX_RECLAIM_LEASES,
+ cfg->getMaxReclaimLeases());
+ EXPECT_EQ(CfgExpiration::DEFAULT_MAX_RECLAIM_TIME,
+ cfg->getMaxReclaimTime());
+ EXPECT_EQ(CfgExpiration::DEFAULT_UNWARNED_RECLAIM_CYCLES,
+ cfg->getUnwarnedReclaimCycles());
+}
+
+// This test verifies that a subset of parameters may be specified and
+// that default values are used for those that aren't specified.
+TEST_F(ExpirationConfigParserTest, someParameters) {
+ addParam("reclaim-timer-wait-time", 15);
+ addParam("hold-reclaimed-time", 2000);
+ addParam("max-reclaim-time", 200);
+
+ CfgExpirationPtr cfg;
+ ASSERT_NO_THROW(cfg = renderConfig());
+ EXPECT_EQ(15, cfg->getReclaimTimerWaitTime());
+ EXPECT_EQ(CfgExpiration::DEFAULT_FLUSH_RECLAIMED_TIMER_WAIT_TIME,
+ cfg->getFlushReclaimedTimerWaitTime());
+ EXPECT_EQ(2000, cfg->getHoldReclaimedTime());
+ EXPECT_EQ(CfgExpiration::DEFAULT_MAX_RECLAIM_LEASES,
+ cfg->getMaxReclaimLeases());
+ EXPECT_EQ(200, cfg->getMaxReclaimTime());
+ EXPECT_EQ(CfgExpiration::DEFAULT_UNWARNED_RECLAIM_CYCLES,
+ cfg->getUnwarnedReclaimCycles());
+}
+
+// This test verifies that another subset of parameters may be specified
+// and that default values are used for those that aren't specified.
+TEST_F(ExpirationConfigParserTest, otherParameters) {
+ addParam("flush-reclaimed-timer-wait-time", 50);
+ addParam("max-reclaim-leases", 60);
+ addParam("unwarned-reclaim-cycles", 20);
+
+ CfgExpirationPtr cfg;
+ ASSERT_NO_THROW(cfg = renderConfig());
+
+ EXPECT_EQ(CfgExpiration::DEFAULT_RECLAIM_TIMER_WAIT_TIME,
+ cfg->getReclaimTimerWaitTime());
+ EXPECT_EQ(50, cfg->getFlushReclaimedTimerWaitTime());
+ EXPECT_EQ(CfgExpiration::DEFAULT_HOLD_RECLAIMED_TIME,
+ cfg->getHoldReclaimedTime());
+ EXPECT_EQ(60, cfg->getMaxReclaimLeases());
+ EXPECT_EQ(CfgExpiration::DEFAULT_MAX_RECLAIM_TIME,
+ cfg->getMaxReclaimTime());
+ EXPECT_EQ(20, cfg->getUnwarnedReclaimCycles());
+}
+
+// This test verifies that the exception is thrown if unsupported
+// parameter is specified.
+TEST_F(ExpirationConfigParserTest, invalidParameter) {
+ addParam("reclaim-timer-wait-time", 20);
+ addParam("invalid-parameter", 20);
+
+ EXPECT_THROW(renderConfig(), DhcpConfigError);
+}
+
+// This test verifies that negative parameter values are not allowed.
+TEST_F(ExpirationConfigParserTest, outOfRangeValues) {
+ testOutOfRange("reclaim-timer-wait-time",
+ CfgExpiration::LIMIT_RECLAIM_TIMER_WAIT_TIME);
+ testOutOfRange("flush-reclaimed-timer-wait-time",
+ CfgExpiration::LIMIT_FLUSH_RECLAIMED_TIMER_WAIT_TIME);
+ testOutOfRange("hold-reclaimed-time",
+ CfgExpiration::LIMIT_HOLD_RECLAIMED_TIME);
+ testOutOfRange("max-reclaim-leases",
+ CfgExpiration::LIMIT_MAX_RECLAIM_LEASES);
+ testOutOfRange("max-reclaim-time",
+ CfgExpiration::LIMIT_MAX_RECLAIM_TIME);
+ testOutOfRange("unwarned-reclaim-cycles",
+ CfgExpiration::LIMIT_UNWARNED_RECLAIM_CYCLES);
+}
+
+// This test verifies that it is not allowed to specify a value as
+// a text.
+TEST_F(ExpirationConfigParserTest, notNumberValue) {
+ // The value should not be in quotes.
+ std::string config = "{ \"reclaim-timer-wait-time\": \"10\" }";
+ ElementPtr config_element = Element::fromJSON(config);
+
+ // Parse the configuration. It should throw exception.
+ ExpirationConfigParser parser;
+ EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+}
+
+} // end of anonymous namespace