Commit 3ac15510 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[2545] Separate the build and commit phases for all parsers.

parent 61c303c8
......@@ -223,55 +223,80 @@ private:
};
/// @brief A boolean value parser.
///
/// This parser handles configuration values of the boolean type.
/// Parsed values are stored in a provided storage. If no storage
/// is provided then the build function throws an exception.
class BooleanParser : public DhcpConfigParser {
public:
/// @brief Constructor.
///
/// @param param_name name of the parameter.
BooleanParser(const std::string& param_name)
: storage_(NULL),
param_name_(param_name),
value_(false) {
}
/// @brief Parse a boolean value.
///
/// @param value a value to be parsed.
///
/// @throw isc::InvalidOperation if a storage has not been set
/// prior to calling this function
/// @throw isc::dhcp::Dhcp6ConfigError if a provided parameter's
/// name is empty.
virtual void build(ConstElementPtr value) {
if (storage_ == NULL) {
isc_throw(isc::InvalidOperation, "parser logic error:"
<< " storage for the " << param_name_
<< " value has not been set");
} else if (param_name_.empty()) {
isc_throw(isc::InvalidOperation, "parser logic error:"
isc_throw(isc::dhcp::Dhcp6ConfigError, "parser logic error:"
<< "empty parameter name provided");
}
// The Config Manager takes care whether a user specified
// valid value for a boolean parameter: True or False.
// It is then ok to assume that if str() does not return
// 'true' the value is 'false'.
value_ = (value->str() == "true") ? true : false;
(*storage_)[param_name_] = value_;
}
virtual void commit() { }
/// @brief Put a parsed value to the storage.
virtual void commit() {
if (storage_ != NULL && !param_name_.empty()) {
(*storage_)[param_name_] = value_;
}
}
/// @brief Create an instance of the boolean parser.
///
/// @brief param_name name of the parameter for which the
/// parser is created.
static DhcpConfigParser* Factory(const std::string& param_name) {
return (new BooleanParser(param_name));
}
/// @brief Set the storage for parsed value.
///
/// This function must be called prior to calling build.
///
/// @param storage a pointer to the storage where parsed data
/// is to be stored.
void setStorage(BooleanStorage* storage) {
storage_ = storage;
}
private:
/// Pointer to the storage where parsed value is stored.
BooleanStorage* storage_;
/// Name of the parameter which value is parsed with this parser.
std::string param_name_;
/// Parsed value.
bool value_;
};
}
namespace isc {
namespace dhcp {
/// @brief Configuration parser for uint32 parameters
///
/// This class is a generic parser that is able to handle any uint32 integer
......@@ -279,10 +304,10 @@ namespace dhcp {
/// (uint32_defaults). If used in smaller scopes (e.g. to parse parameters
/// in subnet config), it can be pointed to a different storage, using
/// setStorage() method. This class follows the parser interface, laid out
/// in its base class, \ref DhcpConfigParser.
/// in its base class, @ref DhcpConfigParser.
///
/// For overview of usability of this generic purpose parser, see
/// \ref dhcpv6ConfigInherit page.
/// @ref dhcpv6ConfigInherit page.
///
/// @todo this class should be turned into the template class which
/// will handle all uintX_types of data (see ticket #2415).
......@@ -290,18 +315,24 @@ class Uint32Parser : public DhcpConfigParser {
public:
/// @brief constructor for Uint32Parser
///
/// @param param_name name of the configuration parameter being parsed
Uint32Parser(const std::string& param_name)
:storage_(&uint32_defaults), param_name_(param_name) {
: storage_(&uint32_defaults),
param_name_(param_name) {
}
/// @brief builds parameter value
///
/// Parses configuration entry and stores it in a storage. See
/// \ref Uint32Parser::setStorage() for details.
/// @brief Parses configuration configuration parameter as uint32_t.
///
/// @param value pointer to the content of parsed values
/// @throw isc::dhcp::Dhcp6ConfigError if failed to parse
/// the configuration parameter as uint32_t value.
virtual void build(ConstElementPtr value) {
if (param_name_.empty()) {
isc_throw(isc::dhcp::Dhcp6ConfigError, "parser logic error:"
<< "empty parameter name provided");
}
bool parse_error = false;
// Cast the provided value to int64 value to check.
int64_t int64value = 0;
......@@ -329,37 +360,32 @@ public:
}
if (parse_error) {
isc_throw(BadValue, "Failed to parse value " << value->str()
isc_throw(isc::dhcp::Dhcp6ConfigError, "Failed to parse value " << value->str()
<< " as unsigned 32-bit integer.");
}
// If a given parameter already exists in the storage we override
// its value. If it doesn't we insert a new element.
(*storage_)[param_name_] = value_;
}
/// @brief does nothing
///
/// This method is required for all parsers. The value itself
/// is not commited anywhere. Higher level parsers are expected to
/// use values stored in the storage, e.g. renew-timer for a given
/// subnet is stored in subnet-specific storage. It is not commited
/// here, but is rather used by \ref Subnet6Parser when constructing
/// the subnet.
virtual void commit() { }
/// @brief Stores the parsed uint32_t value in a storage.
virtual void commit() {
if (storage_ != NULL) {
// If a given parameter already exists in the storage we override
// its value. If it doesn't we insert a new element.
(*storage_)[param_name_] = value_;
}
}
/// @brief factory that constructs Uint32Parser objects
/// @brief Factory that constructs Uint32Parser objects.
///
/// @param param_name name of the parameter to be parsed
/// @param param_name name of the parameter to be parsed.
static DhcpConfigParser* Factory(const std::string& param_name) {
return (new Uint32Parser(param_name));
}
/// @brief sets storage for value of this parameter
/// @brief Sets storage for value of this parameter.
///
/// See \ref dhcpv6ConfigInherit for details.
/// See @ref dhcpv6ConfigInherit for details.
///
/// @param storage pointer to the storage container
/// @param storage pointer to the storage container.
void setStorage(Uint32Storage* storage) {
storage_ = storage;
}
......@@ -367,10 +393,8 @@ public:
private:
/// pointer to the storage, where parsed value will be stored
Uint32Storage* storage_;
/// name of the parameter to be parsed
std::string param_name_;
/// the actual parsed value
uint32_t value_;
};
......@@ -382,54 +406,54 @@ private:
/// (string_defaults). If used in smaller scopes (e.g. to parse parameters
/// in subnet config), it can be pointed to a different storage, using the
/// setStorage() method. This class follows the parser interface, laid out
/// in its base class, \ref DhcpConfigParser.
/// in its base class, @ref DhcpConfigParser.
///
/// For overview of usability of this generic purpose parser, see
/// \ref dhcpv6ConfigInherit page.
/// @ref dhcpv6ConfigInherit page.
class StringParser : public DhcpConfigParser {
public:
/// @brief constructor for StringParser
/// @param param_name name of the configuration parameter being parsed
StringParser(const std::string& param_name)
:storage_(&string_defaults), param_name_(param_name) {
: storage_(&string_defaults),
param_name_(param_name) {
}
/// @brief parses parameter value
///
/// Parses configuration entry and stores it in storage. See
/// \ref setStorage() for details.
/// Parses configuration parameter's value as string.
///
/// @param value pointer to the content of parsed values
/// @throws Dhcp6ConfigError if the parsed parameter's name is empty.
virtual void build(ConstElementPtr value) {
if (param_name_.empty()) {
isc_throw(isc::dhcp::Dhcp6ConfigError, "parser logic error:"
<< "empty parameter name provided");
}
value_ = value->str();
boost::erase_all(value_, "\"");
// If a given parameter already exists in the storage we override
// its value. If it doesn't we insert a new element.
(*storage_)[param_name_] = value_;
}
/// @brief does nothing
///
/// This method is required for all parsers. The value itself
/// is not commited anywhere. Higher level parsers are expected to
/// use values stored in the storage, e.g. renew-timer for a given
/// subnet is stored in subnet-specific storage. It is not commited
/// here, but is rather used by its parent parser when constructing
/// an object, e.g. the subnet.
virtual void commit() { }
/// @brief Stores the parsed value in a storage.
virtual void commit() {
if (storage_ != NULL && !param_name_.empty()) {
// If a given parameter already exists in the storage we override
// its value. If it doesn't we insert a new element.
(*storage_)[param_name_] = value_;
}
}
/// @brief factory that constructs StringParser objects
/// @brief Factory that constructs StringParser objects
///
/// @param param_name name of the parameter to be parsed
static DhcpConfigParser* Factory(const std::string& param_name) {
return (new StringParser(param_name));
}
/// @brief sets storage for value of this parameter
/// @brief Sets storage for value of this parameter.
///
/// See \ref dhcpv6ConfigInherit for details.
/// See @ref dhcpv6ConfigInherit for details.
///
/// @param storage pointer to the storage container
void setStorage(StringStorage* storage) {
......@@ -437,17 +461,14 @@ public:
}
private:
/// pointer to the storage, where parsed value will be stored
/// Pointer to the storage, where parsed value will be stored
StringStorage* storage_;
/// name of the parameter to be parsed
/// Name of the parameter to be parsed
std::string param_name_;
/// the actual parsed value
/// The actual parsed value
std::string value_;
};
/// @brief parser for interface list definition
///
/// This parser handles Dhcp6/interface entry.
......@@ -468,7 +489,7 @@ public:
/// @throw BadValue if supplied parameter name is not "interface"
InterfaceListConfigParser(const std::string& param_name) {
if (param_name != "interface") {
isc_throw(BadValue, "Internal error. Interface configuration "
isc_throw(isc::BadValue, "Internal error. Interface configuration "
"parser called for the wrong parameter: " << param_name);
}
}
......@@ -518,7 +539,7 @@ public:
/// @brief constructor.
PoolParser(const std::string& /*param_name*/)
:pools_(NULL) {
: pools_(NULL) {
// ignore parameter name, it is always Dhcp6/subnet6[X]/pool
}
......@@ -528,11 +549,15 @@ public:
/// No validation is done at this stage, everything is interpreted as
/// interface name.
/// @param pools_list list of pools defined for a subnet
/// @throw BadValue if storage was not specified (setStorage() not called)
/// @throw isc::InvalidOperation if storage was not specified
/// (setStorage() not called)
void build(ConstElementPtr pools_list) {
using namespace isc::dhcp;
// setStorage() should have been called before build
if (!pools_) {
isc_throw(NotImplemented, "Parser logic error. No pool storage set,"
isc_throw(isc::InvalidOperation, "parser logic error: no pool storage set,"
" but pool parser asked to parse pools");
}
......@@ -568,12 +593,12 @@ public:
// be checked in Pool6 constructor.
len = boost::lexical_cast<int>(prefix_len);
} catch (...) {
isc_throw(Dhcp6ConfigError, "Failed to parse pool "
isc_throw(Dhcp6ConfigError, "failed to parse pool "
"definition: " << text_pool->stringValue());
}
Pool6Ptr pool(new Pool6(Pool6::TYPE_IA, addr, len));
pools_->push_back(pool);
local_pools_.push_back(pool);
continue;
}
......@@ -586,11 +611,11 @@ public:
Pool6Ptr pool(new Pool6(Pool6::TYPE_IA, min, max));
pools_->push_back(pool);
local_pools_.push_back(pool);
continue;
}
isc_throw(Dhcp6ConfigError, "Failed to parse pool definition:"
isc_throw(Dhcp6ConfigError, "failed to parse pool definition:"
<< text_pool->stringValue() <<
". Does not contain - (for min-max) nor / (prefix/len)");
}
......@@ -598,19 +623,25 @@ public:
/// @brief sets storage for value of this parameter
///
/// See \ref dhcpv6ConfigInherit for details.
/// See @ref dhcpv6ConfigInherit for details.
///
/// @param storage pointer to the storage container
void setStorage(PoolStorage* storage) {
pools_ = storage;
}
/// @brief does nothing.
/// @brief Stores the parsed values in a storage provided
/// by an upper level parser.
///
/// This method is required for all parsers. The value itself
/// is not commited anywhere. Higher level parsers (for subnet) are expected
/// to use values stored in the storage.
virtual void commit() {}
virtual void commit() {
if (pools_) {
pools_->insert(pools_->end(), local_pools_.begin(),
local_pools_.end());
}
}
/// @brief factory that constructs PoolParser objects
///
......@@ -625,8 +656,12 @@ private:
/// This is typically a storage somewhere in Subnet parser
/// (an upper level parser).
PoolStorage* pools_;
/// A temporary storage for pools configuration. It is a
/// storage where pools are stored by build function.
PoolStorage local_pools_;
};
/// @brief Parser for option data value.
///
/// This parser parses configuration entries that specify value of
......@@ -669,6 +704,9 @@ public:
/// calling build.
/// @throw isc::BadValue if option data storage is invalid.
virtual void build(ConstElementPtr option_data_entries) {
using namespace isc::dhcp;
if (options_ == NULL) {
isc_throw(isc::InvalidOperation, "Parser logic error: storage must be set before "
"parsing option data.");
......@@ -709,6 +747,7 @@ public:
<< param.first);
}
parser->build(param.second);
parser->commit();
}
// Try to create the option instance.
createOption();
......@@ -733,11 +772,11 @@ public:
" thus there is nothing to commit. Has build() been called?");
}
uint16_t opt_type = option_descriptor_.option->getType();
Subnet::OptionContainerTypeIndex& idx = options_->get<1>();
isc::dhcp::Subnet::OptionContainerTypeIndex& idx = options_->get<1>();
// Try to find options with the particular option code in the main
// storage. If found, remove these options because they will be
// replaced with new one.
Subnet::OptionContainerTypeRange range =
isc::dhcp::Subnet::OptionContainerTypeRange range =
idx.equal_range(opt_type);
if (std::distance(range.first, range.second) > 0) {
idx.erase(range.first, range.second);
......@@ -774,6 +813,9 @@ private:
/// @throw Dhcp6ConfigError if parameters provided in the configuration
/// are invalid.
void createOption() {
using namespace isc::dhcp;
// Option code is held in the uint32_t storage but is supposed to
// be uint16_t value. We need to check that value in the configuration
// does not exceed range of uint16_t and is not zero.
......@@ -808,7 +850,7 @@ private:
} else {
// Transform string of hexadecimal digits into binary format.
try {
util::encode::decodeHex(option_data, binary);
isc::util::encode::decodeHex(option_data, binary);
} catch (...) {
isc_throw(Dhcp6ConfigError, "Parser error: option data is not a valid"
<< " string of hexadecimal digits: " << option_data);
......@@ -876,7 +918,7 @@ private:
std::string getStringParam(const std::string& param_id) const {
StringStorage::const_iterator param = string_values_.find(param_id);
if (param == string_values_.end()) {
isc_throw(Dhcp6ConfigError, "parser error: option-data parameter"
isc_throw(isc::dhcp::Dhcp6ConfigError, "parser error: option-data parameter"
<< " '" << param_id << "' not specified");
}
return (param->second);
......@@ -889,7 +931,7 @@ private:
uint32_t getUint32Param(const std::string& param_id) const {
Uint32Storage::const_iterator param = uint32_values_.find(param_id);
if (param == uint32_values_.end()) {
isc_throw(Dhcp6ConfigError, "parser error: option-data parameter"
isc_throw(isc::dhcp::Dhcp6ConfigError, "parser error: option-data parameter"
<< " '" << param_id << "' not specified");
}
return (param->second);
......@@ -898,7 +940,7 @@ private:
bool getBooleanParam(const std::string& param_id) const {
BooleanStorage::const_iterator param = boolean_values_.find(param_id);
if (param == boolean_values_.end()) {
isc_throw(Dhcp6ConfigError, "parser error: option-data parameter"
isc_throw(isc::dhcp::Dhcp6ConfigError, "parser error: option-data parameter"
<< " '" << param_id << "' not specified");
}
return (param->second);
......@@ -908,12 +950,13 @@ private:
Uint32Storage uint32_values_;
/// Storage for string values (e.g. option name or data).
StringStorage string_values_;
/// Storage for boolean values.
BooleanStorage boolean_values_;
/// Pointer to options storage. This storage is provided by
/// the calling class and is shared by all OptionDataParser objects.
OptionStorage* options_;
/// Option descriptor holds newly configured option.
Subnet::OptionDescriptor option_descriptor_;
isc::dhcp::Subnet::OptionDescriptor option_descriptor_;
};
/// @brief Parser for option data values within a subnet.
......@@ -994,6 +1037,12 @@ public:
ParserCollection parsers_;
};
}
namespace isc {
namespace dhcp {
/// @brief this class parses a single subnet
///
/// This class parses the whole subnet definition. It creates parsers
......@@ -1038,21 +1087,20 @@ public:
isc_throw(Dhcp6ConfigError, "failed to find suitable parser");
}
}
// Ok, we now have subnet parsed
}
/// @brief commits received configuration.
///
/// This method does most of the configuration. Many other parsers are just
/// storing the values that are actually consumed here. Pool definitions
/// created in other parsers are used here and added to newly created Subnet6
/// objects. Subnet6 are then added to DHCP CfgMgr.
void commit() {
// Invoke commit on all sub-data parsers.
// In order to create new subnet we need to get the data out
// of the child parsers first. The only way to do it is to
// invoke commit on them because it will make them write
// parsed data into storages we have supplied.
// Note that triggering commits on child parsers does not
// affect global data because we supplied pointers to storages
// local to this object. Thus, even if this method fails
// later on, the configuration remains consistent.
BOOST_FOREACH(ParserPtr parser, parsers_) {
parser->commit();
}
// Create a subnet.
StringStorage::const_iterator it = string_values_.find("subnet");
if (it == string_values_.end()) {
isc_throw(Dhcp6ConfigError,
......@@ -1067,6 +1115,7 @@ public:
isc_throw(Dhcp6ConfigError,
"Invalid subnet syntax (prefix/len expected):" << it->second);
}
IOAddress addr(subnet_txt.substr(0, pos));
uint8_t len = boost::lexical_cast<unsigned int>(subnet_txt.substr(pos + 1));
......@@ -1083,13 +1132,13 @@ public:
LOG_INFO(dhcp6_logger, DHCP6_CONFIG_NEW_SUBNET).arg(tmp.str());
Subnet6Ptr subnet(new Subnet6(addr, len, t1, t2, pref, valid));
subnet_.reset(new Subnet6(addr, len, t1, t2, pref, valid));
for (PoolStorage::iterator it = pools_.begin(); it != pools_.end(); ++it) {
subnet->addPool6(*it);
subnet_->addPool6(*it);
}
const Subnet::OptionContainer& options = subnet->getOptions();
const Subnet::OptionContainer& options = subnet_->getOptions();
const Subnet::OptionContainerTypeIndex& idx = options.get<1>();
// Add subnet specific options.
......@@ -1099,7 +1148,7 @@ public:
LOG_WARN(dhcp6_logger, DHCP6_CONFIG_OPTION_DUPLICATE)
.arg(desc.option->getType()).arg(addr.toText());
}
subnet->addOption(desc.option);
subnet_->addOption(desc.option);
}
// Check all global options and add them to the subnet object if
......@@ -1119,11 +1168,21 @@ public:
// want to issue a warning about dropping the configuration of
// a global option if one already exsists.
if (std::distance(range.first, range.second) == 0) {
subnet->addOption(desc.option);
subnet_->addOption(desc.option);
}
}
}
CfgMgr::instance().addSubnet6(subnet);
/// @brief commits received configuration.
///
/// This method does most of the configuration. Many other parsers are just
/// storing the values that are actually consumed here. Pool definitions
/// created in other parsers are used here and added to newly created Subnet6
/// objects. Subnet6 are then added to DHCP CfgMgr.
void commit() {
if (subnet_) {
CfgMgr::instance().addSubnet6(subnet_);
}
}
private:
......@@ -1172,24 +1231,13 @@ private:
DhcpConfigParser* createSubnet6ConfigParser(const std::string& config_id) {
FactoryMap factories;
factories.insert(pair<string, ParserFactory*>(
"preferred-lifetime", Uint32Parser::Factory));
factories.insert(pair<string, ParserFactory*>(
"valid-lifetime", Uint32Parser::Factory));
factories.insert(pair<string, ParserFactory*>(
"renew-timer", Uint32Parser::Factory));
factories.insert(pair<string, ParserFactory*>(
"rebind-timer", Uint32Parser::Factory));
factories.insert(pair<string, ParserFactory*>(
"subnet", StringParser::Factory));
factories.insert(pair<string, ParserFactory*>(
"pool", PoolParser::Factory));
factories.insert(pair<string, ParserFactory*>(
"option-data", OptionDataListParser::Factory));
factories["preferred-lifetime"] = Uint32Parser::Factory;
factories["valid-lifetime"] = Uint32Parser::Factory;
factories["renew-timer"] = Uint32Parser::Factory;
factories["rebind-timer"] = Uint32Parser::Factory;
factories["subnet"] = StringParser::Factory;
factories["pool"] = PoolParser::Factory;
factories["option-data"] = OptionDataListParser::Factory;
FactoryMap::iterator f = factories.find(config_id);
if (f == factories.end()) {
......@@ -1250,6 +1298,9 @@ private:
/// parsers are stored here
ParserCollection parsers_;
/// Pointer to the created subnet object.
Subnet6Ptr subnet_;
};
/// @brief this class parses a list of subnets
......@@ -1325,25 +1376,14 @@ public:
DhcpConfigParser* createGlobalDhcpConfigParser(const std::string& config_id) {
FactoryMap factories;