#include <dhcpsrv/parsers/host_reservation_parser.h>
#include <dhcpsrv/parsers/host_reservations_list_parser.h>
#include <dhcpsrv/parsers/ifaces_config_parser.h>
+#include <dhcpsrv/parsers/simple_parser.h>
#include <dhcpsrv/timer_mgr.h>
#include <config/command_mgr.h>
#include <util/encode/hex.h>
parser = new Pools4ListParser(config_id, pools_);
} else if (config_id.compare("relay") == 0) {
parser = new RelayInfoParser(config_id, relay_info_, Option::V4);
- } else if (config_id.compare("option-data") == 0) {
- parser = new OptionDataListParser(config_id, options_, AF_INET);
+ // option-data has been converted to SimpleParser already.
} else if (config_id.compare("match-client-id") == 0) {
parser = new BooleanParser(config_id, boolean_values_);
} else if (config_id.compare("4o6-subnet") == 0) {
parser = new IfacesConfigParser4();
} else if (config_id.compare("subnet4") == 0) {
parser = new Subnets4ListConfigParser(config_id);
- } else if (config_id.compare("option-data") == 0) {
- parser = new OptionDataListParser(config_id, CfgOptionPtr(), AF_INET);
- } else if (config_id.compare("option-def") == 0) {
- parser = new OptionDefListParser(config_id, globalContext());
+ // option-data and option-def have been converted to SimpleParser already.
} else if ((config_id.compare("version") == 0) ||
(config_id.compare("next-server") == 0)) {
parser = new StringParser(config_id,
// Please do not change this order!
ParserCollection independent_parsers;
ParserPtr subnet_parser;
- ParserPtr option_parser;
ParserPtr iface_parser;
ParserPtr leases_parser;
ParserPtr client_classes_parser;
// the name of the failing parser can be retrieved in the "catch" clause.
ConfigPair config_pair;
try {
+
+ // This is a way to convert ConstElementPtr to ElementPtr.
+ // We need a config that can be edited, because we will insert
+ // default values and will insert derived values as well.
+ std::map<std::string, ConstElementPtr> values;
+ config_set->getValue(values);
+ ElementPtr mutable_cfg(new MapElement());
+ mutable_cfg->setValue(values);
+
+ // Set all default values if not specified by the user.
+ SimpleParser::setAllDefaults(mutable_cfg, false);
+
+ // We need definitions first
+ ConstElementPtr option_defs = mutable_cfg->get("option-def");
+ if (option_defs) {
+ OptionDefListParser parser(AF_INET);
+ CfgOptionDefPtr cfg_option_def = CfgMgr::instance().getStagingCfg()->getCfgOptionDef();
+ parser.parse(cfg_option_def, option_defs);
+ }
+
// Make parsers grouping.
const std::map<std::string, ConstElementPtr>& values_map =
- config_set->mapValue();
+ mutable_cfg->mapValue();
BOOST_FOREACH(config_pair, values_map) {
+
+ if (config_pair.first == "option-def") {
+ // This is converted to SimpleParser and is handled already above.
+ continue;
+ }
+
+ if (config_pair.first == "option-data") {
+ OptionDataListParser parser(AF_INET);
+ CfgOptionPtr cfg_option = CfgMgr::instance().getStagingCfg()->getCfgOption();
+ parser.parse(cfg_option, config_pair.second);
+ continue;
+ }
+
ParserPtr parser(createGlobalDhcp4ConfigParser(config_pair.first,
config_pair.second));
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PARSER_CREATED)
subnet_parser = parser;
} else if (config_pair.first == "lease-database") {
leases_parser = parser;
- } else if (config_pair.first == "option-data") {
- option_parser = parser;
} else if (config_pair.first == "interfaces-config") {
// The interface parser is independent from any other
// parser and can be run here before any other parsers.
}
}
- // The option values parser is the next one to be run.
- std::map<std::string, ConstElementPtr>::const_iterator option_config =
- values_map.find("option-data");
- if (option_config != values_map.end()) {
- config_pair.first = "option-data";
- option_parser->build(option_config->second);
- option_parser->commit();
- }
-
// The class definitions parser is the next one to be run.
std::map<std::string, ConstElementPtr>::const_iterator cc_config =
values_map.find("client-classes");
"\"valid-lifetime\": 600,"
"\"option-data\": [ {"
" \"name\": \"vivso-suboptions\","
- " \"data\": 4491"
+ " \"data\": \"4491\""
"},"
"{"
" \"name\": \"tftp-servers\","
" \"ip-address\": \"10.0.0.7\","
" \"option-data\": [ {"
" \"name\": \"vivso-suboptions\","
- " \"data\": 4491"
+ " \"data\": \"4491\""
" },"
" {"
" \"name\": \"tftp-servers\","
#include <dhcpsrv/parsers/host_reservation_parser.h>
#include <dhcpsrv/parsers/host_reservations_list_parser.h>
#include <dhcpsrv/parsers/ifaces_config_parser.h>
+#include <dhcpsrv/parsers/simple_parser.h>
#include <log/logger_support.h>
#include <util/encode/hex.h>
#include <util/strutil.h>
uint32_values_));
parser = code_parser;
} else if (entry == "option-data") {
- OptionDataListParserPtr option_parser(new OptionDataListParser(entry,
- options_,
- AF_INET6));
- parser = option_parser;
+ OptionDataListParser opts_parser(AF_INET6);
+ opts_parser.parse(options_, param.second);
+ // OptionDataListParser is converted to SimpleParser already,
+ // no need to go through build/commit phases.
+ continue;
} else {
isc_throw(DhcpConfigError, "unsupported parameter: " << entry
<< " (" << param.second->getPosition() << ")");
parser = new RelayInfoParser(config_id, relay_info_, Option::V6);
} else if (config_id.compare("pd-pools") == 0) {
parser = new PdPoolListParser(config_id, pools_);
- } else if (config_id.compare("option-data") == 0) {
- parser = new OptionDataListParser(config_id, options_, AF_INET6);
+ // option-data was here, but it is now converted to SimpleParser
} else if (config_id.compare("rapid-commit") == 0) {
parser = new BooleanParser(config_id, boolean_values_);
} else {
parser = new IfacesConfigParser6();
} else if (config_id.compare("subnet6") == 0) {
parser = new Subnets6ListConfigParser(config_id);
- } else if (config_id.compare("option-data") == 0) {
- parser = new OptionDataListParser(config_id, CfgOptionPtr(), AF_INET6);
- } else if (config_id.compare("option-def") == 0) {
- parser = new OptionDefListParser(config_id, globalContext());
- } else if (config_id.compare("version") == 0) {
+ // option-data and option-def are no longer needed here. They're now
+ // converted to SimpleParser and are handled in configureDhcp6Server
+ } else if (config_id.compare("version") == 0) {
parser = new StringParser(config_id,
globalContext()->string_values_);
} else if (config_id.compare("lease-database") == 0) {
// Please do not change this order!
ParserCollection independent_parsers;
ParserPtr subnet_parser;
- ParserPtr option_parser;
ParserPtr iface_parser;
ParserPtr leases_parser;
ParserPtr client_classes_parser;
ConfigPair config_pair;
try {
+ // This is a way to convert ConstElementPtr to ElementPtr.
+ // We need a config that can be edited, because we will insert
+ // default values and will insert derived values as well.
+ std::map<std::string, ConstElementPtr> values;
+ config_set->getValue(values);
+ ElementPtr mutable_cfg(new MapElement());
+ mutable_cfg->setValue(values);
+
+ SimpleParser::setAllDefaults(mutable_cfg, true);
+
// Make parsers grouping.
const std::map<std::string, ConstElementPtr>& values_map =
- config_set->mapValue();
+ mutable_cfg->mapValue();
+
+ // We need definitions first
+ ConstElementPtr option_defs = mutable_cfg->get("option-def");
+ if (option_defs) {
+ OptionDefListParser parser(AF_INET6);
+ CfgOptionDefPtr cfg_option_def = CfgMgr::instance().getStagingCfg()->getCfgOptionDef();
+ parser.parse(cfg_option_def, option_defs);
+ }
+
BOOST_FOREACH(config_pair, values_map) {
+
+ if (config_pair.first == "option-def") {
+ // This is converted to SimpleParser and is handled already above.
+ continue;
+ }
+
+ if (config_pair.first == "option-data") {
+ OptionDataListParser parser(AF_INET6);
+ CfgOptionPtr cfg_option = CfgMgr::instance().getStagingCfg()->getCfgOption();
+ parser.parse(cfg_option, config_pair.second);
+ continue;
+ }
+
ParserPtr parser(createGlobal6DhcpConfigParser(config_pair.first,
config_pair.second));
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PARSER_CREATED)
subnet_parser = parser;
} else if (config_pair.first == "lease-database") {
leases_parser = parser;
- } else if (config_pair.first == "option-data") {
+ } /* else if (config_pair.first == "option-data") {
option_parser = parser;
- } else if (config_pair.first == "hooks-libraries") {
+ } */ else if (config_pair.first == "hooks-libraries") {
// Executing the commit will alter currently loaded hooks
// libraries. Check if the supplied libraries are valid,
// but defer the commit until after everything else has
}
}
- // The option values parser is the next one to be run.
- std::map<std::string, ConstElementPtr>::const_iterator option_config =
- values_map.find("option-data");
- if (option_config != values_map.end()) {
- config_pair.first = "option-data";
- option_parser->build(option_config->second);
- option_parser->commit();
- }
-
// The class definitions parser is the next one to be run.
std::map<std::string, ConstElementPtr>::const_iterator cc_config =
values_map.find("client-classes");
" \"option-def\": [ {"
" \"name\": \"foo\","
" \"code\": 120,"
+ " \"csv-format\": false,"
" \"type\": \"binary\""
" } ],"
" \"option-data\": [ {"
" \"code\": 120,"
+ " \"csv-format\": false,"
" \"data\": \"05\""
" } ],"
" \"preferred-lifetime\": 3000,"
"\"renew-timer\": 1000, "
"\"option-data\": [ {"
" \"name\": \"vendor-opts\","
- " \"data\": 4491"
+ " \"data\": \"4491\""
"},"
"{"
" \"name\": \"tftp-servers\","
" \"ip-addresses\": [ \"2001:db8:1::2\" ],"
" \"option-data\": [ {"
" \"name\": \"vendor-opts\","
- " \"data\": 4491"
+ " \"data\": \"4491\""
" },"
" {"
" \"name\": \"tftp-servers\","
EXTRA_DIST += parsers/client_class_def_parser.cc
EXTRA_DIST += parsers/client_class_def_parser.h
EXTRA_DIST += parsers/dhcp_config_parser.h
+EXTRA_DIST += parsers/simple_parser.cc
+EXTRA_DIST += parsers/simple_parser.h
EXTRA_DIST += parsers/dbaccess_parser.cc
EXTRA_DIST += parsers/dbaccess_parser.h
EXTRA_DIST += parsers/dhcp_parsers.cc
libkea_dhcpsrv_la_SOURCES += parsers/dbaccess_parser.h
libkea_dhcpsrv_la_SOURCES += parsers/dhcp_parsers.cc
libkea_dhcpsrv_la_SOURCES += parsers/dhcp_parsers.h
+libkea_dhcpsrv_la_SOURCES += parsers/simple_parser.cc
+libkea_dhcpsrv_la_SOURCES += parsers/simple_parser.h
libkea_dhcpsrv_la_SOURCES += parsers/duid_config_parser.cc
libkea_dhcpsrv_la_SOURCES += parsers/duid_config_parser.h
libkea_dhcpsrv_la_SOURCES += parsers/expiration_config_parser.cc
global_context_));
parser = exp_parser;
} else if (entry == "option-data") {
- OptionDataListParserPtr opts_parser;
+
uint16_t family = (global_context_->universe_ == Option::V4 ?
AF_INET : AF_INET6);
- opts_parser.reset(new OptionDataListParser(entry, options_, family));
- parser = opts_parser;
+ OptionDataListParser opts_parser(family);
+ opts_parser.parse(options_, param.second);
+
+ // OptionDataListParser is converted to SimpleParser already,
+ // no need to go through build/commit phases.
+ continue;
} else if (entry == "next-server") {
StringParserPtr str_parser(new StringParser(entry, string_values_));
parser = str_parser;
}
// **************************** OptionDataParser *************************
-OptionDataParser::OptionDataParser(const std::string&, const CfgOptionPtr& cfg,
- const uint16_t address_family)
- : boolean_values_(new BooleanStorage()),
- string_values_(new StringStorage()), uint32_values_(new Uint32Storage()),
- option_descriptor_(false), cfg_(cfg),
- address_family_(address_family) {
- // If configuration not specified, then it is a global configuration
- // scope.
- if (!cfg_) {
- cfg_ = CfgMgr::instance().getStagingCfg()->getCfgOption();
- }
+OptionDataParser::OptionDataParser(const uint16_t address_family)
+ : address_family_(address_family) {
}
-void
-OptionDataParser::build(ConstElementPtr option_data_entries) {
- BOOST_FOREACH(ConfigPair param, option_data_entries->mapValue()) {
- ParserPtr parser;
- if (param.first == "name" || param.first == "data" ||
- param.first == "space") {
- StringParserPtr name_parser(new StringParser(param.first,
- string_values_));
- parser = name_parser;
- } else if (param.first == "code") {
- Uint32ParserPtr code_parser(new Uint32Parser(param.first,
- uint32_values_));
- parser = code_parser;
- } else if (param.first == "csv-format") {
- BooleanParserPtr value_parser(new BooleanParser(param.first,
- boolean_values_));
- parser = value_parser;
- } else {
- isc_throw(DhcpConfigError,
- "option-data parameter not supported: " << param.first
- << " (" << param.second->getPosition() << ")");
- }
-
- parser->build(param.second);
- // Before we can create an option we need to get the data from
- // the child parsers. The only way to do it is to invoke commit
- // on them so as they store the values in appropriate storages
- // that this class provided to them. Note that this will not
- // modify values stored in the global storages so the configuration
- // will remain consistent even parsing fails somewhere further on.
- parser->commit();
- }
+std::pair<OptionDescriptor, std::string>
+OptionDataParser::parse(isc::data::ConstElementPtr single_option) {
// Try to create the option instance.
- createOption(option_data_entries);
+ std::pair<OptionDescriptor, std::string> opt = createOption(single_option);
- if (!option_descriptor_.option_) {
+ if (!opt.first.option_) {
isc_throw(isc::InvalidOperation,
"parser logic error: no option has been configured and"
" thus there is nothing to commit. Has build() been called?");
}
- cfg_->add(option_descriptor_.option_, option_descriptor_.persistent_,
- option_space_);
-}
-
-void
-OptionDataParser::commit() {
- // Does nothing
+ return (opt);
}
OptionalValue<uint32_t>
OptionDataParser::extractCode(ConstElementPtr parent) const {
uint32_t code;
try {
- code = uint32_values_->getParam("code");
+ code = getInteger(parent, "code");
} catch (const exception&) {
// The code parameter was not found. Return an unspecified
if (code == 0) {
isc_throw(DhcpConfigError, "option code must not be zero "
- "(" << uint32_values_->getPosition("code", parent) << ")");
+ "(" << getPosition("code", parent) << ")");
} else if (address_family_ == AF_INET &&
code > std::numeric_limits<uint8_t>::max()) {
isc_throw(DhcpConfigError, "invalid option code '" << code
<< "', it must not be greater than '"
<< static_cast<int>(std::numeric_limits<uint8_t>::max())
- << "' (" << uint32_values_->getPosition("code", parent)
+ << "' (" << getPosition("code", parent)
<< ")");
} else if (address_family_ == AF_INET6 &&
isc_throw(DhcpConfigError, "invalid option code '" << code
<< "', it must not exceed '"
<< std::numeric_limits<uint16_t>::max()
- << "' (" << uint32_values_->getPosition("code", parent)
+ << "' (" << getPosition("code", parent)
<< ")");
}
OptionDataParser::extractName(ConstElementPtr parent) const {
std::string name;
try {
- name = string_values_->getParam("name");
+ name = getString(parent, "name");
} catch (...) {
return (OptionalValue<std::string>());
if (name.find(" ") != std::string::npos) {
isc_throw(DhcpConfigError, "invalid option name '" << name
<< "', space character is not allowed ("
- << string_values_->getPosition("name", parent) << ")");
+ << getPosition("name", parent) << ")");
}
return (OptionalValue<std::string>(name, OptionalValueState(true)));
}
std::string
-OptionDataParser::extractData() const {
+OptionDataParser::extractData(ConstElementPtr parent) const {
std::string data;
try {
- data = string_values_->getParam("data");
+ data = getString(parent, "data");
} catch (...) {
// The "data" parameter was not found. Return an empty value.
}
OptionalValue<bool>
-OptionDataParser::extractCSVFormat() const {
+OptionDataParser::extractCSVFormat(ConstElementPtr parent) const {
bool csv_format = true;
try {
- csv_format = boolean_values_->getParam("csv-format");
+ csv_format = getBoolean(parent, "csv-format");
} catch (...) {
return (OptionalValue<bool>(csv_format));
}
std::string
-OptionDataParser::extractSpace() const {
+OptionDataParser::extractSpace(ConstElementPtr parent) const {
std::string space = address_family_ == AF_INET ? "dhcp4" : "dhcp6";
try {
- space = string_values_->getParam("space");
+ space = getString(parent, "space");
} catch (...) {
return (space);
// should never get here. Therefore, it is ok to call getPosition for
// the space parameter here as this parameter will always be specified.
isc_throw(DhcpConfigError, ex.what() << " ("
- << string_values_->getPosition("space") << ")");
+ << getPosition("space", parent) << ")");
}
return (space);
return (def);
}
-void
+std::pair<OptionDescriptor, std::string>
OptionDataParser::createOption(ConstElementPtr option_data) {
const Option::Universe universe = address_family_ == AF_INET ?
Option::V4 : Option::V6;
OptionalValue<uint32_t> code_param = extractCode(option_data);
OptionalValue<std::string> name_param = extractName(option_data);
- OptionalValue<bool> csv_format_param = extractCSVFormat();
- std::string data_param = extractData();
- std::string space_param = extractSpace();
+ OptionalValue<bool> csv_format_param = extractCSVFormat(option_data);
+ std::string data_param = extractData(option_data);
+ std::string space_param = extractSpace(option_data);
// Require that option code or option name is specified.
if (!code_param.isSpecified() && !name_param.isSpecified()) {
<< space_param << "." << name_param
<< "' having code '" << code_param
<< "' does not exist ("
- << string_values_->getPosition("name", option_data)
+ << getPosition("name", option_data)
<< ")");
// If there is no option definition and the option code is not specified
isc_throw(DhcpConfigError, "definition for the option '"
<< space_param << "." << name_param
<< "' does not exist ("
- << string_values_->getPosition("name", option_data)
+ << getPosition("name", option_data)
<< ")");
}
}
isc_throw(DhcpConfigError, "option data is not a valid"
<< " string of hexadecimal digits: " << data_param
<< " ("
- << string_values_->getPosition("data", option_data)
+ << getPosition("data", option_data)
<< ")");
}
}
OptionPtr option;
+ OptionDescriptor desc(false);
+
if (!def) {
// @todo We have a limited set of option definitions initalized at
// the moment. In the future we want to initialize option definitions
// ok to create generic option if definition does not exist.
OptionPtr option(new Option(universe, static_cast<uint16_t>(code_param),
binary));
- // The created option is stored in option_descriptor_ class member
- // until the commit stage when it is inserted into the main storage.
- // If an option with the same code exists in main storage already the
- // old option is replaced.
- option_descriptor_.option_ = option;
- option_descriptor_.persistent_ = false;
+ desc.option_ = option;
+ desc.persistent_ = false;
} else {
// Option name is specified it should match the name in the definition.
<< name_param << "' does not match the "
<< "option definition: '" << space_param
<< "." << def->getName() << "' ("
- << string_values_->getPosition("name", option_data)
+ << getPosition("name", option_data)
<< ")");
}
!csv_format_param.isSpecified() || csv_format_param ?
def->optionFactory(universe, def->getCode(), data_tokens) :
def->optionFactory(universe, def->getCode(), binary);
- OptionDescriptor desc(option, false);
- option_descriptor_.option_ = option;
- option_descriptor_.persistent_ = false;
-
+ desc.option_ = option;
+ desc.persistent_ = false;
} catch (const isc::Exception& ex) {
isc_throw(DhcpConfigError, "option data does not match"
<< " option definition (space: " << space_param
<< ", code: " << def->getCode() << "): "
<< ex.what() << " ("
- << string_values_->getPosition("data", option_data)
+ << getPosition("data", option_data)
<< ")");
}
}
// All went good, so we can set the option space name.
- option_space_ = space_param;
+ return make_pair(desc, space_param);
}
// **************************** OptionDataListParser *************************
-OptionDataListParser::OptionDataListParser(const std::string&,
- const CfgOptionPtr& cfg,
+OptionDataListParser::OptionDataListParser(//const std::string&,
+ //const CfgOptionPtr& cfg,
const uint16_t address_family)
- : cfg_(cfg), address_family_(address_family) {
+ : address_family_(address_family) {
}
-void
-OptionDataListParser::build(ConstElementPtr option_data_list) {
- BOOST_FOREACH(ConstElementPtr option_value, option_data_list->listValue()) {
- boost::shared_ptr<OptionDataParser>
- parser(new OptionDataParser("option-data", cfg_, address_family_));
-
- parser->build(option_value);
- parsers_.push_back(parser);
- }
-}
-void
-OptionDataListParser::commit() {
- BOOST_FOREACH(ParserPtr parser, parsers_) {
- parser->commit();
- }
- // Append suboptions to the top-level options
- if (cfg_) {
- cfg_->encapsulate();
- } else {
- CfgMgr::instance().getStagingCfg()->getCfgOption()->encapsulate();
+void OptionDataListParser::parse(const CfgOptionPtr& cfg,
+ isc::data::ConstElementPtr option_data_list) {
+ OptionDataParser option_parser(address_family_);
+ BOOST_FOREACH(ConstElementPtr data, option_data_list->listValue()) {
+ std::pair<OptionDescriptor, std::string> option =
+ option_parser.parse(data);
+ cfg->add(option.first.option_, option.first.persistent_, option.second);
+ cfg->encapsulate();
}
}
// ******************************** OptionDefParser ****************************
-OptionDefParser::OptionDefParser(const std::string&,
- ParserContextPtr global_context)
- : boolean_values_(new BooleanStorage()),
- string_values_(new StringStorage()),
- uint32_values_(new Uint32Storage()),
- global_context_(global_context) {
+OptionDefParser::OptionDefParser(const uint16_t address_family)
+ : address_family_(address_family) {
}
-void
-OptionDefParser::build(ConstElementPtr option_def) {
- // Parse the elements that make up the option definition.
- BOOST_FOREACH(ConfigPair param, option_def->mapValue()) {
- std::string entry(param.first);
- ParserPtr parser;
- if (entry == "name" || entry == "type" || entry == "record-types"
- || entry == "space" || entry == "encapsulate") {
- StringParserPtr str_parser(new StringParser(entry,
- string_values_));
- parser = str_parser;
- } else if (entry == "code") {
- Uint32ParserPtr code_parser(new Uint32Parser(entry,
- uint32_values_));
- parser = code_parser;
- } else if (entry == "array") {
- BooleanParserPtr array_parser(new BooleanParser(entry,
- boolean_values_));
- parser = array_parser;
- } else {
- isc_throw(DhcpConfigError, "invalid parameter '" << entry
- << "' (" << param.second->getPosition() << ")");
- }
+std::pair<isc::dhcp::OptionDefinitionPtr, std::string>
+OptionDefParser::parse(ConstElementPtr option_def) {
- parser->build(param.second);
- parser->commit();
- }
- // Create an instance of option definition.
- createOptionDef(option_def);
+ // Get mandatory parameters.
+ std::string name = getString(option_def, "name");
+ uint32_t code = getInteger(option_def, "code");
+ std::string type = getString(option_def, "type");
- try {
- CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->
- add(option_definition_, option_space_name_);
-
- } catch (const std::exception& ex) {
- // Append position if there is a failure.
- isc_throw(DhcpConfigError, ex.what() << " ("
- << option_def->getPosition() << ")");
- }
-
- // All definitions have been prepared. Put them as runtime options into
- // the libdhcp++.
- const OptionDefSpaceContainer& container =
- CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->getContainer();
- LibDHCP::setRuntimeOptionDefs(container);
-}
-
-void
-OptionDefParser::commit() {
- // Do nothing.
-}
-
-void
-OptionDefParser::createOptionDef(ConstElementPtr option_def_element) {
- // Check if mandatory parameters have been specified.
- std::string name;
- uint32_t code;
- std::string type;
- try {
- name = string_values_->getParam("name");
- code = uint32_values_->getParam("code");
- type = string_values_->getParam("type");
- } catch (const std::exception& ex) {
- isc_throw(DhcpConfigError, ex.what() << " ("
- << option_def_element->getPosition() << ")");
- }
-
- bool array_type = boolean_values_->getOptionalParam("array", false);
- std::string record_types =
- string_values_->getOptionalParam("record-types", "");
- std::string space = string_values_->getOptionalParam("space",
- global_context_->universe_ == Option::V4 ? "dhcp4" : "dhcp6");
- std::string encapsulates =
- string_values_->getOptionalParam("encapsulate", "");
+ // Get optional parameters. Whoever called this parser, should have
+ // called SimpleParser::setDefaults first.
+ bool array_type = getBoolean(option_def, "array");
+ std::string record_types = getString(option_def, "record-types");
+ std::string space = getString(option_def, "space");
+ std::string encapsulates = getString(option_def, "encapsulate");
if (!OptionSpace::validateName(space)) {
isc_throw(DhcpConfigError, "invalid option space name '"
<< space << "' ("
- << string_values_->getPosition("space") << ")");
+ << getPosition("space", option_def) << ")");
}
// Create option definition.
isc_throw(DhcpConfigError, "option '" << space << "."
<< "name" << "', comprising an array of data"
<< " fields may not encapsulate any option space ("
- << option_def_element->getPosition() << ")");
+ << option_def->getPosition() << ")");
} else if (encapsulates == space) {
isc_throw(DhcpConfigError, "option must not encapsulate"
<< " an option space it belongs to: '"
<< space << "." << name << "' is set to"
<< " encapsulate '" << space << "' ("
- << option_def_element->getPosition() << ")");
+ << option_def->getPosition() << ")");
} else {
def.reset(new OptionDefinition(name, code, type,
isc_throw(DhcpConfigError, "invalid record type values"
<< " specified for the option definition: "
<< ex.what() << " ("
- << string_values_->getPosition("record-types") << ")");
+ << getPosition("record-types", option_def) << ")");
}
}
def->validate();
} catch (const std::exception& ex) {
isc_throw(DhcpConfigError, ex.what()
- << " (" << option_def_element->getPosition() << ")");
+ << " (" << option_def->getPosition() << ")");
}
// Option definition has been created successfully.
- option_space_name_ = space;
- option_definition_ = def;
+ return make_pair(def, space);
}
// ******************************** OptionDefListParser ************************
-OptionDefListParser::OptionDefListParser(const std::string&,
- ParserContextPtr global_context)
- : global_context_(global_context) {
+OptionDefListParser::OptionDefListParser(const uint16_t address_family)
+ : address_family_(address_family) {
}
void
-OptionDefListParser::build(ConstElementPtr option_def_list) {
+OptionDefListParser::parse(CfgOptionDefPtr storage, ConstElementPtr option_def_list) {
if (!option_def_list) {
isc_throw(DhcpConfigError, "parser error: a pointer to a list of"
<< " option definitions is NULL ("
<< option_def_list->getPosition() << ")");
}
+ OptionDefParser parser(address_family_);
BOOST_FOREACH(ConstElementPtr option_def, option_def_list->listValue()) {
- boost::shared_ptr<OptionDefParser>
- parser(new OptionDefParser("single-option-def", global_context_));
- parser->build(option_def);
+ OptionDefinitionTuple def;
+
+ def = parser.parse(option_def);
+ try {
+ storage->add(def.first, def.second);
+ } catch (const std::exception& ex) {
+ // Append position if there is a failure.
+ isc_throw(DhcpConfigError, ex.what() << " ("
+ << option_def->getPosition() << ")");
+ }
}
-}
-void
-OptionDefListParser::commit() {
- // Do nothing.
+ // All definitions have been prepared. Put them as runtime options into
+ // the libdhcp++.
+ LibDHCP::setRuntimeOptionDefs(storage->getContainer());
}
//****************************** RelayInfoParser ********************************
" address pools");
}
- OptionDataListParserPtr option_parser(new OptionDataListParser("option-data",
- options_,
- address_family_));
- option_parser->build(option_data);
- option_parser->commit();
- options_->copyTo(*pool->getCfgOption());;
-
+ CfgOptionPtr cfg = pool->getCfgOption();
+ OptionDataListParser option_parser(address_family_);
+ option_parser.parse(cfg, option_data);
} catch (const std::exception& ex) {
isc_throw(isc::dhcp::DhcpConfigError, ex.what()
<< " (" << option_data->getPosition() << ")");
continue;
}
+ if (param.first == "option-data") {
+ uint16_t family = global_context_->universe_ == Option::V4 ? AF_INET : AF_INET6;
+ OptionDataListParser opt_parser(family);
+ opt_parser.parse(options_, param.second);
+ continue;
+ }
+
ParserPtr parser;
// When unsupported parameter is specified, the function called
// below will thrown an exception. We have to catch this exception
#include <dhcpsrv/cfg_iface.h>
#include <dhcpsrv/cfg_option.h>
#include <dhcpsrv/subnet.h>
+#include <dhcpsrv/cfg_option_def.h>
#include <dhcpsrv/parsers/dhcp_config_parser.h>
+#include <dhcpsrv/parsers/simple_parser.h>
#include <hooks/libinfo.h>
#include <exceptions/exceptions.h>
#include <util/optional_value.h>
/// an option the configuration will not be accepted. If parsing
/// is successful then an instance of an option is created and
/// added to the storage provided by the calling class.
-class OptionDataParser : public DhcpConfigParser {
+class OptionDataParser : public SimpleParser {
public:
/// @brief Constructor.
///
- /// @param dummy first argument is ignored, all Parser constructors
- /// accept string as first argument.
- /// @param [out] cfg Pointer to the configuration object where parsed option
- /// should be stored or NULL if this is a global option.
/// @param address_family Address family: @c AF_INET or @c AF_INET6.
- /// @throw isc::dhcp::DhcpConfigError if options or global_context are null.
- OptionDataParser(const std::string& dummy, const CfgOptionPtr& cfg,
- const uint16_t address_family);
+ OptionDataParser(const uint16_t address_family);
- /// @brief Parses the single option data.
+ /// @brief Parses ElementPtr containing option definition
///
- /// This method parses the data of a single option from the configuration.
- /// The option data includes option name, option code and data being
- /// carried by this option. Eventually it creates the instance of the
- /// option and adds it to the Configuration Manager.
+ /// This method parses ElementPtr containing the option defintion,
+ /// instantiates the option for it and then returns a pair
+ /// of option descritor (that holds that new option) and
+ /// a string that specifies the option space.
///
- /// @param option_data_entries collection of entries that define value
- /// for a particular option.
- /// @throw DhcpConfigError if invalid parameter specified in
- /// the configuration.
- /// @throw isc::InvalidOperation if failed to set storage prior to
- /// calling build.
- virtual void build(isc::data::ConstElementPtr option_data_entries);
-
- /// @brief Does nothing.
- virtual void commit();
-
- /// @brief virtual destructor to ensure orderly destruction of derivations.
- virtual ~OptionDataParser(){};
+ /// Note: ElementPtr is expected to contain all fields. If your
+ /// ElementPtr does not have them, please use
+ /// @ref SimpleParser::setOptionDefaults to fill the missing fields
+ /// with default values.
+ ///
+ /// @param single_option ElementPtr containing option defintion
+ /// @return Option object wrapped in option description and an option
+ /// space
+ std::pair<OptionDescriptor, std::string>
+ parse(isc::data::ConstElementPtr single_option);
private:
/// @brief Finds an option definition within an option space
/// options storage. If the option data parsed by \ref build function
/// are invalid or insufficient this function emits an exception.
///
- /// @warning this function does not check if options_ storage pointer
- /// is intitialized but this check is not needed here because it is done
- /// in the \ref build function.
- ///
/// @param option_data An element holding data for a single option being
/// created.
///
+ /// @return created option descriptor
+ ///
/// @throw DhcpConfigError if parameters provided in the configuration
/// are invalid.
- void createOption(isc::data::ConstElementPtr option_data);
+ std::pair<OptionDescriptor, std::string>
+ createOption(isc::data::ConstElementPtr option_data);
/// @brief Retrieves parsed option code as an optional value.
///
/// @param parent A data element holding full option data configuration.
- /// It is used here to log a position if the element holding a code
- /// is not specified and its position is therefore unavailable.
///
/// @return Option code, possibly unspecified.
/// @throw DhcpConfigError if option code is invalid.
/// @brief Retrieves parsed option name as an optional value.
///
/// @param parent A data element holding full option data configuration.
- /// It is used here to log a position if the element holding a name
- /// is not specified and its position is therefore unavailable.
///
/// @return Option name, possibly unspecified.
/// @throw DhcpConfigError if option name is invalid.
/// @brief Retrieves csv-format parameter as an optional value.
///
/// @return Value of the csv-format parameter, possibly unspecified.
- util::OptionalValue<bool> extractCSVFormat() const;
+ util::OptionalValue<bool> extractCSVFormat(data::ConstElementPtr parent) const;
/// @brief Retrieves option data as a string.
///
+ /// @param parent A data element holding full option data configuration.
/// @return Option data as a string. It will return empty string if
/// option data is unspecified.
- std::string extractData() const;
+ std::string extractData(data::ConstElementPtr parent) const;
/// @brief Retrieves option space name.
///
/// 'dhcp4' or 'dhcp6' option space name is returned, depending on
/// the universe specified in the parser context.
///
+ /// @param parent A data element holding full option data configuration.
+ ///
/// @return Option space name.
- std::string extractSpace() const;
-
- /// Storage for boolean values.
- BooleanStoragePtr boolean_values_;
-
- /// Storage for string values (e.g. option name or data).
- StringStoragePtr string_values_;
-
- /// Storage for uint32 values (e.g. option code).
- Uint32StoragePtr uint32_values_;
-
- /// Option descriptor holds newly configured option.
- OptionDescriptor option_descriptor_;
-
- /// Option space name where the option belongs to.
- std::string option_space_;
-
- /// @brief Configuration holding option being parsed or NULL if the option
- /// is global.
- CfgOptionPtr cfg_;
+ std::string extractSpace(data::ConstElementPtr parent) const;
/// @brief Address family: @c AF_INET or @c AF_INET6.
uint16_t address_family_;
/// data for a particular subnet and creates a collection of options.
/// If parsing is successful, all these options are added to the Subnet
/// object.
-class OptionDataListParser : public DhcpConfigParser {
+class OptionDataListParser : public SimpleParser {
public:
/// @brief Constructor.
///
- /// @param dummy nominally would be param name, this is always ignored.
- /// @param [out] cfg Pointer to the configuration object where options
- /// should be stored or NULL if this is global option scope.
/// @param address_family Address family: @c AF_INET or AF_INET6
- OptionDataListParser(const std::string& dummy, const CfgOptionPtr& cfg,
- const uint16_t address_family);
+ OptionDataListParser(const uint16_t address_family);
- /// @brief Parses entries that define options' data for a subnet.
+ /// @brief Parses a list of options, instantiates them and stores in cfg
///
- /// This method iterates over all entries that define option data
- /// for options within a single subnet and creates options' instances.
- ///
- /// @param option_data_list pointer to a list of options' data sets.
- /// @throw DhcpConfigError if option parsing failed.
- void build(isc::data::ConstElementPtr option_data_list);
-
- /// @brief Commit all option values.
+ /// This method expects to get a list of options in option_data_list,
+ /// iterates over them, creates option objects, wraps them with
+ /// option descriptor and stores in specified cfg.
///
- /// This function invokes commit for all option values
- /// and append suboptions to the top-level options.
- void commit();
-
+ /// @param cfg created options will be stored here
+ /// @param option_data_list configuration that describes the options
+ void parse(const CfgOptionPtr& cfg,
+ isc::data::ConstElementPtr option_data_list);
private:
-
- /// Collection of parsers;
- ParserCollection parsers_;
-
- /// @brief Pointer to a configuration where options are stored.
- CfgOptionPtr cfg_;
-
/// @brief Address family: @c AF_INET or @c AF_INET6
uint16_t address_family_;
-
};
-typedef boost::shared_ptr<OptionDataListParser> OptionDataListParserPtr;
-
+typedef std::pair<isc::dhcp::OptionDefinitionPtr, std::string> OptionDefinitionTuple;
/// @brief Parser for a single option definition.
///
/// This parser creates an instance of a single option definition.
-class OptionDefParser : public DhcpConfigParser {
+class OptionDefParser : public SimpleParser {
public:
/// @brief Constructor.
///
- /// @param dummy first argument is ignored, all Parser constructors
- /// accept string as first argument.
- /// @param global_context is a pointer to the global context which
- /// stores global scope parameters, options, option defintions.
- OptionDefParser(const std::string& dummy, ParserContextPtr global_context);
+ /// @param address_family Address family: @c AF_INET or AF_INET6
+ OptionDefParser(const uint16_t address_family);
/// @brief Parses an entry that describes single option definition.
///
/// @param option_def a configuration entry to be parsed.
+ /// @return tuple (option definition, option space) of the parsed structure
///
/// @throw DhcpConfigError if parsing was unsuccessful.
- void build(isc::data::ConstElementPtr option_def);
-
- /// @brief Stores the parsed option definition in a storage.
- void commit();
+ OptionDefinitionTuple
+ parse(isc::data::ConstElementPtr option_def);
private:
-
- /// @brief Create option definition from the parsed parameters.
- ///
- /// @param option_def_element A data element holding the configuration
- /// for an option definition.
- void createOptionDef(isc::data::ConstElementPtr option_def_element);
-
- /// Instance of option definition being created by this parser.
- OptionDefinitionPtr option_definition_;
-
- /// Name of the space the option definition belongs to.
- std::string option_space_name_;
-
- /// Storage for boolean values.
- BooleanStoragePtr boolean_values_;
-
- /// Storage for string values.
- StringStoragePtr string_values_;
-
- /// Storage for uint32 values.
- Uint32StoragePtr uint32_values_;
-
- /// Parsing context which contains global values, options and option
- /// definitions.
- ParserContextPtr global_context_;
+ /// @brief Address family: @c AF_INET or @c AF_INET6
+ uint16_t address_family_;
};
/// @brief Parser for a list of option definitions.
/// option definitions and creates instances of these definitions.
/// If the parsing is successful, the collection of created definitions
/// is put into the provided storage.
-class OptionDefListParser : public DhcpConfigParser {
+class OptionDefListParser : public SimpleParser {
public:
/// @brief Constructor.
///
/// accept string as first argument.
/// @param global_context is a pointer to the global context which
/// stores global scope parameters, options, option defintions.
- OptionDefListParser(const std::string& dummy,
- ParserContextPtr global_context);
+ OptionDefListParser(const uint16_t address_family);
+ //const std::string& dummy,
+ //ParserContextPtr global_context);
- /// @brief Parse configuration entries.
+ /// @brief Parses a list of option defintions, create them and store in cfg
///
- /// This function parses configuration entries, creates instances
- /// of option definitions and tries to add them to the Configuration
- /// Manager.
+ /// This method iterates over def_list, which is a JSON list of option defintions,
+ /// then creates corresponding option defintions and store them in the
+ /// configuration structure.
///
- /// @param option_def_list pointer to an element that holds entries
- /// that define option definitions.
- /// @throw DhcpConfigError if configuration parsing fails.
- void build(isc::data::ConstElementPtr option_def_list);
+ /// @param def_list JSON list describing option definitions
+ /// @param cfg parsed option definitions will be stored here
+ void parse(CfgOptionDefPtr cfg, isc::data::ConstElementPtr def_list);
- /// @brief Commits option definitions.
- ///
- /// Currently this function is no-op, because option definitions are
- /// added to the Configuration Manager in the @c build method.
- void commit();
+protected:
- /// Parsing context which contains global values, options and option
- /// definitions.
- ParserContextPtr global_context_;
+ /// @brief Address family: @c AF_INET or @c AF_INET6
+ uint16_t address_family_;
};
/// @brief a collection of pools
// surround it with try-catch.
if (element.first == "option-data") {
CfgOptionPtr cfg_option = host_->getCfgOption4();
- OptionDataListParser parser(element.first, cfg_option, AF_INET);
- parser.build(element.second);
+
+ // This parser is converted to SimpleParser already. It
+ // parses the Element structure immediately, there's no need
+ // to go through build/commit phases.
+ OptionDataListParser parser(AF_INET);
+ parser.parse(cfg_option, element.second);
// Everything else should be surrounded with try-catch to append
// position.
// appended).
if (element.first == "option-data") {
CfgOptionPtr cfg_option = host_->getCfgOption6();
- OptionDataListParser parser(element.first, cfg_option, AF_INET6);
- parser.build(element.second);
+
+ // This parser is converted to SimpleParser already. It
+ // parses the Element structure immediately, there's no need
+ // to go through build/commit phases.
+ OptionDataListParser parser(AF_INET6);
+ parser.parse(cfg_option, element.second);
} else if (element.first == "ip-addresses" || element.first == "prefixes") {
BOOST_FOREACH(ConstElementPtr prefix_element,
--- /dev/null
+#include <dhcpsrv/parsers/simple_parser.h>
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+#include <cc/data.h>
+#include <string>
+
+using namespace std;
+using namespace isc::data;
+
+namespace isc {
+namespace dhcp {
+
+/// This table defines default values for option definitions in DHCPv4
+const SimpleDefaults OPTION4_DEF_DEFAULTS{
+ { "record-types", Element::string, ""},
+ { "space", Element::string, "dhcp4"},
+ { "array", Element::boolean, "false"},
+ { "encapsulate", Element::string, "" }
+};
+
+/// This table defines default values for option definitions in DHCPv6
+const SimpleDefaults OPTION6_DEF_DEFAULTS{
+ { "record-types", Element::string, ""},
+ { "space", Element::string, "dhcp6"},
+ { "array", Element::boolean, "false"},
+ { "encapsulate", Element::string, "" }
+};
+
+/// This table defines default values for options in DHCPv4
+const SimpleDefaults OPTION4_DEFAULTS({
+ { "space", Element::string, "dhcp4"},
+ { "csv-format", Element::boolean, "true"},
+ { "encapsulate", Element::string, "" }
+});
+
+/// This table defines default values for options in DHCPv6
+const SimpleDefaults OPTION6_DEFAULTS({
+ { "space", Element::string, "dhcp6"},
+ { "csv-format", Element::boolean, "true"},
+ { "encapsulate", Element::string, "" }
+});
+
+/// This table defines default values for both DHCPv4 and DHCPv6
+const SimpleDefaults GLOBAL_DEFAULTS = {
+ { "renew-timer", Element::integer, "900" },
+ { "rebind-timer", Element::integer, "1800" },
+ { "preferred-lifetime", Element::integer, "3600" },
+ { "valid-lifetime", Element::integer, "7200" }
+};
+
+std::string
+SimpleParser::getString(isc::data::ConstElementPtr scope, const std::string& name) {
+ ConstElementPtr x = scope->get(name);
+ if (!x) {
+ isc_throw(BadValue, "Element " << name << " not found");
+ }
+ if (x->getType() != Element::string) {
+ isc_throw(BadValue, "Element " << name << " found, but is not a string");
+ }
+
+ return (x->stringValue());
+}
+
+int64_t
+SimpleParser::getInteger(isc::data::ConstElementPtr scope, const std::string& name) {
+ ConstElementPtr x = scope->get(name);
+ if (!x) {
+ isc_throw(BadValue, "Element " << name << " not found");
+ }
+ if (x->getType() != Element::integer) {
+ isc_throw(BadValue, "Element " << name << " found, but is not an integer");
+ }
+
+ return (x->intValue());
+}
+
+bool
+SimpleParser::getBoolean(isc::data::ConstElementPtr scope, const std::string& name) {
+ ConstElementPtr x = scope->get(name);
+ if (!x) {
+ isc_throw(BadValue, "Element " << name << " not found");
+ }
+ if (x->getType() != Element::boolean) {
+ isc_throw(BadValue, "Element " << name << " found, but is not a boolean");
+ }
+
+ return (x->boolValue());
+}
+
+const data::Element::Position&
+SimpleParser::getPosition(const std::string& name, const data::ConstElementPtr parent) const {
+ if (!parent) {
+ return (data::Element::ZERO_POSITION());
+ }
+ ConstElementPtr elem = parent->get(name);
+ if (!elem) {
+ return (data::Element::ZERO_POSITION());
+ }
+ return (elem->getPosition());
+}
+
+size_t SimpleParser::setDefaults(isc::data::ElementPtr scope,
+ const SimpleDefaults& default_values) {
+ size_t cnt = 0;
+
+ // This is the position representing a default value. As the values
+ // we're inserting here are not present in whatever the config file
+ // came from, we need to make sure it's clearly labeled as default.
+ const Element::Position pos("<default-value>", 0, 0);
+
+ // Let's go over all parameters we have defaults for.
+ BOOST_FOREACH(SimpleDefault def_value, default_values) {
+
+ // Try if such a parameter is there. If it is, let's
+ // skip it, because user knows best *cough*.
+ ConstElementPtr x = scope->get(string(def_value.name_));
+ if (x) {
+ // There is such a value already, skip it.
+ continue;
+ }
+
+ // There isn't such a value defined, let's create the default
+ // value...
+ switch (def_value.type_) {
+ case Element::string: {
+ x.reset(new StringElement(def_value.value_, pos));
+ break;
+ }
+ case Element::integer: {
+ int int_value = boost::lexical_cast<int>(def_value.value_);
+ x.reset(new IntElement(int_value, pos));
+ break;
+ }
+ case Element::boolean: {
+ bool bool_value;
+ if (def_value.value_ == string("true")) {
+ bool_value = true;
+ } else if (def_value.value_ == string("false")) {
+ bool_value = false;
+ } else {
+ isc_throw(BadValue, "Internal error. Boolean value specified as "
+ << def_value.value_ << ", expected true or false");
+ }
+ x.reset(new BoolElement(bool_value, pos));
+ break;
+ }
+ case Element::real: {
+ double dbl_value = boost::lexical_cast<double>(def_value.value_);
+ x.reset(new DoubleElement(dbl_value, pos));
+ break;
+ }
+ default:
+ // No default values for null, list or map
+ isc_throw(BadValue, "Internal error. Incorrect default value type.");
+ }
+
+ // ... and insert it into the provided Element tree.
+ scope->set(def_value.name_, x);
+ cnt++;
+ }
+
+ return (cnt);
+}
+
+size_t SimpleParser::setGlobalDefaults(isc::data::ElementPtr global) {
+ return (setDefaults(global, GLOBAL_DEFAULTS));
+}
+
+size_t SimpleParser::setOptionDefaults(isc::data::ElementPtr option, bool v6) {
+ return (setDefaults(option, v6?OPTION6_DEFAULTS : OPTION4_DEFAULTS));
+}
+
+size_t SimpleParser::setOptionListDefaults(isc::data::ElementPtr option_list, bool v6) {
+ size_t cnt = 0;
+ BOOST_FOREACH(ElementPtr single_option, option_list->listValue()) {
+ cnt += setOptionDefaults(single_option, v6);
+ }
+ return (cnt);
+}
+
+size_t SimpleParser::setOptionDefDefaults(isc::data::ElementPtr option_def, bool v6) {
+ return (setDefaults(option_def, v6? OPTION6_DEF_DEFAULTS : OPTION4_DEF_DEFAULTS));
+}
+
+size_t SimpleParser::setOptionDefListDefaults(isc::data::ElementPtr option_def_list,
+ bool v6) {
+ size_t cnt = 0;
+ BOOST_FOREACH(ElementPtr single_def, option_def_list->listValue()) {
+ cnt += setOptionDefDefaults(single_def, v6);
+ }
+ return (cnt);
+}
+
+
+size_t SimpleParser::setAllDefaults(isc::data::ElementPtr global, bool v6) {
+ size_t cnt = 0;
+
+ // Set global defaults first.
+ //cnt = setGlobalDefaults(global);
+
+ // Now set option defintion defaults for each specified option definition
+ ConstElementPtr option_defs = global->get("option-def");
+ if (option_defs) {
+ BOOST_FOREACH(ElementPtr single_def, option_defs->listValue()) {
+ cnt += setOptionDefDefaults(single_def, v6);
+ }
+ }
+
+ ConstElementPtr options = global->get("option-data");
+ if (options) {
+ BOOST_FOREACH(ElementPtr single_option, options->listValue()) {
+ cnt += setOptionDefaults(single_option, v6);
+ }
+ //setOptionListDefaults(options);
+ }
+
+ return (cnt);
+}
+
+size_t
+SimpleParser::deriveParams(isc::data::ConstElementPtr parent,
+ isc::data::ElementPtr child,
+ const ParamsList& params) {
+ if ( (parent->getType() != Element::map) ||
+ (child->getType() != Element::map)) {
+ return (0);
+ }
+
+ size_t cnt = 0;
+ BOOST_FOREACH(string param, params) {
+ ConstElementPtr x = parent->get(param);
+ if (!x) {
+ // Parent doesn't define this parameter, so there's
+ // nothing to derive from
+ continue;
+ }
+
+ if (child->get(param)) {
+ // Child defines this parameter already. There's
+ // nothing to do here.
+ continue;
+ }
+
+ // Copy the parameters to the child scope.
+ child->set(param, x);
+ cnt++;
+ }
+
+ return (cnt);
+}
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
--- /dev/null
+#ifndef SIMPLE_PARSER_H
+#define SIMPLE_PARSER_H
+
+#include <cc/data.h>
+#include <string>
+#include <stdint.h>
+
+namespace isc {
+namespace dhcp {
+
+/// This array defines a single entry of default values
+struct SimpleDefault {
+ SimpleDefault(const char* name, isc::data::Element::types type, const char* value)
+ :name_(name), type_(type), value_(value) {}
+ std::string name_;
+ const isc::data::Element::types type_;
+ const char* value_;
+};
+
+/// This specifies all default values in a given scope (e.g. a subnet)
+typedef std::vector<SimpleDefault> SimpleDefaults;
+
+/// This defines a list of all parameters that are derived (or inherited) between
+/// contexts
+typedef std::vector<std::string> ParamsList;
+
+
+/// @brief A simple parser
+///
+/// This class is intended to be a simpler replacement for @ref DhcpConfigParser.
+/// The simplification comes from several factors:
+/// - no build/commit nonsense. There's a single step:
+/// CfgStorage parse(ConstElementPtr json)
+/// that converts JSON configuration into an object and returns it.
+/// - almost no state kept. The only state kept in most cases is whether the
+/// parsing is done in v4 or v6 context. This greatly simplifies the
+/// parsers (no contexts, no child parsers list, no separate storage for
+/// uint32, strings etc. In fact, there's so little state kept, that this
+/// parser is mostly a collection of static methods.
+/// - no optional parameters (all are mandatory). This simplifies the parser,
+/// but introduces a new step before parsing where we insert the default
+/// values into client configuration before parsing. This is actually a good
+/// thing, because we now have a clear picture of the default parameters as
+/// they're defined in a single place (the DhcpConfigParser had the defaults
+/// spread out in multiple files in multiple directories).
+class SimpleParser {
+ public:
+
+ /// @brief Derives (inherits) parameters from parent scope to a child
+ ///
+ /// This method derives parameters from the parent scope to the child,
+ /// if there are no values specified in the child scope. For example,
+ /// this method can be used to derive timers from global scope (e.g. for
+ /// the whole DHCPv6 server) to a subnet scope. This method checks
+ /// if the child scope doesn't have more specific values defined. If
+ /// it doesn't, then the value from parent scope is copied over.
+ ///
+ /// @param parent scope to copy from (e.g. global)
+ /// @param child scope to copy from (e.g. subnet)
+ /// @param params names of the parameters to copy
+ /// @return number of parameters copied
+ static size_t deriveParams(isc::data::ConstElementPtr parent,
+ isc::data::ElementPtr child,
+ const ParamsList& params);
+
+ /// @brief Sets the default values
+ ///
+ /// This method sets the default values for parameters that are not
+ /// defined. The list of default values is specified by default_values.
+ /// If not present, those will be inserted into the scope. If
+ /// a parameter is already present, the default value will not
+ /// be inserted.
+ ///
+ /// @param scope default values will be inserted here
+ /// @param default_values list of default values
+ /// @return number of parameters inserted
+ static size_t setDefaults(isc::data::ElementPtr scope,
+ const SimpleDefaults& default_values);
+
+ /// @brief Sets global defaults
+ ///
+ /// This method sets global defaults. Note it does not set the
+ /// defaults for any scopes lower than global. See @ref setAllDefaults
+ /// for that.
+ ///
+ /// @param global scope to be filled in with defaults.
+ /// @return number of default values added
+ static size_t setGlobalDefaults(isc::data::ElementPtr global);
+
+ /// @brief Sets option defaults for a single option
+ ///
+ /// This method sets default values for a single option.
+ ///
+ /// @param option an option data to be filled in with defaults.
+ /// @param v6 is it v6 (true) or v4 (false) option?
+ /// @return number of default values added
+ static size_t setOptionDefaults(isc::data::ElementPtr option, bool v6);
+
+ /// @brief Sets option defaults for the whole options list
+ ///
+ /// This method sets default values for a list of options.
+ ///
+ /// @param option_list an option data to be filled in with defaults.
+ /// @param v6 is it v6 (true) or v4 (false) option?
+ /// @return number of default values added
+ static size_t setOptionListDefaults(isc::data::ElementPtr option_list, bool v6);
+
+ /// @brief Sets option defaults for a single option definition
+ ///
+ /// This method sets default values for a single option definition.
+ ///
+ /// @param option_def an option defintion to be filled in with defaults.
+ /// @param v6 is it v6 (true) or v4 (false) option defintion?
+ /// @return number of default values added
+ static size_t setOptionDefDefaults(isc::data::ElementPtr option_def, bool v6);
+
+ /// @brief Sets option defaults for the whole option definitions list
+ ///
+ /// This method sets default values for a list of option definitions.
+ ///
+ /// @param option_def_list a list of option defintions to be filled in with defaults.
+ /// @param v6 is it v6 (true) or v4 (false) option?
+ /// @return number of default values added
+ static size_t setOptionDefListDefaults(isc::data::ElementPtr option_def_list,
+ bool v6);
+
+ /// @brief Sets defaults for the whole configuration
+ ///
+ /// This method sets global, options and option definition defaults. Note
+ /// it does set the defaults for scopes lower than global. If you want to
+ /// set the global defaults only, see @ref setGlobalDefaults for that.
+ ///
+ /// @param global scope to be filled in with defaults.
+ /// @return number of default values added
+ static size_t setAllDefaults(isc::data::ElementPtr global, bool v6);
+
+ /// @brief Utility method that returns position of an element
+ ///
+ /// It's mostly useful for logging.
+ ///
+ /// @param name position of that element will be returned
+ /// @param parent parent element (optional)
+ /// @return position of the element specified.
+ const data::Element::Position&
+ getPosition(const std::string& name, const data::ConstElementPtr parent =
+ data::ConstElementPtr()) const;
+
+ /// @destructor
+ ///
+ /// It's really simple, isn't it?
+ virtual ~SimpleParser() {}
+
+ protected:
+
+ /// @brief Returns a string parameter from a scope
+ ///
+ /// Unconditionally returns a parameter. If the parameter is not there or
+ /// is not of appropriate type, BadValue exception is thrown.
+ ///
+ /// @param scope specified parameter will be extracted from this scope
+ /// @param name name of the parameter
+ /// @return a string value of the parameter
+ static std::string getString(isc::data::ConstElementPtr scope, const std::string& name);
+
+ /// @brief Returns an integer parameter from a scope
+ ///
+ /// Unconditionally returns a parameter. If the parameter is not there or
+ /// is not of appropriate type, BadValue exception is thrown.
+ ///
+ /// @param scope specified parameter will be extracted from this scope
+ /// @param name name of the parameter
+ /// @return an integer value of the parameter
+ static int64_t getInteger(isc::data::ConstElementPtr scope, const std::string& name);
+
+ /// @brief Returns a boolean parameter from a scope
+ ///
+ /// Unconditionally returns a parameter. If the parameter is not there or
+ /// is not of appropriate type, BadValue exception is thrown.
+ ///
+ /// @param scope specified parameter will be extracted from this scope
+ /// @param name name of the parameter
+ /// @return a boolean value of the parameter
+ static bool getBoolean(isc::data::ConstElementPtr scope, const std::string& name);
+};
+
+};
+};
+
+#endif
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/cfg_mac_source.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
+#include <dhcpsrv/parsers/simple_parser.h>
#include <dhcpsrv/tests/test_libraries.h>
#include <dhcpsrv/testutils/config_result_check.h>
#include <exceptions/exceptions.h>
/// @return returns an ConstElementPtr containing the numeric result
/// code and outcome comment.
isc::data::ConstElementPtr parseElementSet(isc::data::ConstElementPtr
- config_set) {
+ config_set) {
// Answer will hold the result.
ConstElementPtr answer;
if (!config_set) {
const std::map<std::string, ConstElementPtr>& values_map =
config_set->mapValue();
BOOST_FOREACH(config_pair, values_map) {
+
+ // These are the simple parsers. No need to go through
+ // the ParserPtr hooplas with them.
+ if ( (config_pair.first == "option-data") ||
+ (config_pair.first == "option-def")) {
+ continue;
+ }
+ // Remaining ones are old style parsers. Need go do
+ // the build/commit dance with them.
+
// Create the parser based on element name.
ParserPtr parser(createConfigParser(config_pair.first));
// Options must be parsed last
}
}
+ int family = parser_context_->universe_ == Option::V4
+ ? AF_INET : AF_INET6;
+
+ // The option definition parser is the next one to be run.
+ std::map<std::string, ConstElementPtr>::const_iterator
+ def_config = values_map.find("option-def");
+ if (def_config != values_map.end()) {
+
+ CfgOptionDefPtr cfg_def = CfgMgr::instance().getStagingCfg()->getCfgOptionDef();
+ OptionDefListParser def_list_parser(family);
+ def_list_parser.parse(cfg_def, def_config->second);
+ }
+
// The option values parser is the next one to be run.
std::map<std::string, ConstElementPtr>::const_iterator
option_config = values_map.find("option-data");
if (option_config != values_map.end()) {
- option_parser->build(option_config->second);
- option_parser->commit();
+ CfgOptionPtr cfg_option = CfgMgr::instance().getStagingCfg()->getCfgOption();
+
+ OptionDataListParser option_list_parser(family);
+ option_list_parser.parse(cfg_option, option_config->second);
}
// Everything was fine. Configuration is successful.
/// @throw throws NotImplemented if element name isn't supported.
ParserPtr createConfigParser(const std::string& config_id) {
ParserPtr parser;
- int family = parser_context_->universe_ == Option::V4 ?
- AF_INET : AF_INET6;
- if (config_id.compare("option-data") == 0) {
- parser.reset(new OptionDataListParser(config_id, CfgOptionPtr(),
- family));
-
- } else if (config_id.compare("option-def") == 0) {
- parser.reset(new OptionDefListParser(config_id,
- parser_context_));
-
- } else if (config_id.compare("hooks-libraries") == 0) {
+ // option-data and option-def converted to SimpleParser, so they
+ // are no longer here.
+ if (config_id.compare("hooks-libraries") == 0) {
parser.reset(new HooksLibrariesParser(config_id));
hooks_libraries_parser_ =
boost::dynamic_pointer_cast<HooksLibrariesParser>(parser);
///
/// @return retuns 0 if the configuration parsed successfully,
/// non-zero otherwise failure.
- int parseConfiguration(const std::string& config) {
+ int parseConfiguration(const std::string& config, bool v6 = false) {
int rcode_ = 1;
// Turn config into elements.
// Test json just to make sure its valid.
ElementPtr json = Element::fromJSON(config);
EXPECT_TRUE(json);
if (json) {
+ SimpleParser::setAllDefaults(json, v6);
+
ConstElementPtr status = parseElementSet(json);
ConstElementPtr comment = parseAnswer(rcode_, status);
error_text_ = comment->stringValue();
"}";
// Verify that the configuration string parses.
- int rcode = parseConfiguration(config);
+ int rcode = parseConfiguration(config, true);
ASSERT_EQ(0, rcode);
" } ]"
"}";
int rcode = 0;
- ASSERT_NO_THROW(rcode = parseConfiguration(config));
+ ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
EXPECT_NE(0, rcode);
CfgMgr::instance().clear();
" \"data\": \"0\""
" } ]"
"}";
- ASSERT_NO_THROW(rcode = parseConfiguration(config));
+ ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
EXPECT_NE(0, rcode);
CfgMgr::instance().clear();
" \"data\": \"0\""
" } ]"
"}";
- ASSERT_NO_THROW(rcode = parseConfiguration(config));
+ ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
ASSERT_EQ(0, rcode);
OptionPtr opt = getOptionPtr("dhcp6", 25000);
ASSERT_TRUE(opt);
" \"name\": \"foo-name\","
" \"space\": \"dhcp6\","
" \"code\": 25000,"
+ " \"csv-format\": false,"
" \"data\": \"123456\""
" } ]"
"}";
- ASSERT_NO_THROW(rcode = parseConfiguration(config));
+ ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
EXPECT_EQ(0, rcode);
opt = getOptionPtr("dhcp6", 25000);
ASSERT_TRUE(opt);
" } ]"
"}";
int rcode = 0;
- ASSERT_NO_THROW(rcode = parseConfiguration(config));
+ ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
EXPECT_EQ(0, rcode);
Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
Option6AddrLst>(getOptionPtr("dhcp6", 23));
" } ]"
"}";
int rcode = 0;
- ASSERT_NO_THROW(rcode = parseConfiguration(config));
+ ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
EXPECT_EQ(0, rcode);
Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
Option6AddrLst>(getOptionPtr("dhcp6", 23));
" } ]"
"}";
int rcode = 0;
- ASSERT_NO_THROW(rcode = parseConfiguration(config));
+ ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
EXPECT_EQ(0, rcode);
Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
Option6AddrLst>(getOptionPtr("dhcp6", 23));
" } ]"
"}";
rcode = 0;
- ASSERT_NO_THROW(rcode = parseConfiguration(config));
+ ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
EXPECT_EQ(0, rcode);
opt = boost::dynamic_pointer_cast<Option6AddrLst>(getOptionPtr("dhcp6",
23));
"}";
int rcode = 0;
- ASSERT_NO_THROW(rcode = parseConfiguration(config));
+ ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
EXPECT_EQ(0, rcode);
Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
Option6AddrLst>(getOptionPtr("dhcp6", 2345));
"}";
rcode = 0;
- ASSERT_NO_THROW(rcode = parseConfiguration(config));
+ ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
EXPECT_EQ(0, rcode);
opt = boost::dynamic_pointer_cast<Option6AddrLst>(getOptionPtr("dhcp6",
2345));
"}";
int rcode = 0;
- ASSERT_NO_THROW(rcode = parseConfiguration(config));
+ ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
EXPECT_EQ(0, rcode);
const Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
Option6AddrLst>(getOptionPtr("dhcp6", D6O_DHCPV4_O_DHCPV6_SERVER));