2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-31 05:55:28 +00:00

[5014_phase2] SimpleParser implemented, 4 parsers converted

- SimpleParser concept implemented
 - Converted 4 parsers (option data, option data list, option defintion,
   option definition list)
 - updated unit-tests
 - converted other parsers (HostReservationParser{4,6}, ClientClassDefParser)
   to use those new parsers
 - converted kea-dhcp{4,6} to use those new parsers

Conflicts:
	src/bin/dhcp6/json_config_parser.cc
	src/lib/dhcpsrv/parsers/dhcp_parsers.cc
This commit is contained in:
Tomek Mrugalski
2016-11-29 20:09:14 +01:00
parent 04fa0d3f0b
commit f9abab4bd0
13 changed files with 759 additions and 434 deletions

View File

@@ -20,6 +20,7 @@
#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>
@@ -190,8 +191,7 @@ protected:
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) {
@@ -424,10 +424,7 @@ DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id,
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,
@@ -538,7 +535,6 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
// 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;
@@ -567,10 +563,43 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
// 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)
@@ -579,8 +608,6 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
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.
@@ -606,15 +633,6 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
}
}
// 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");

View File

@@ -203,7 +203,7 @@ const char* HOST_CONFIGS[] = {
"\"valid-lifetime\": 600,"
"\"option-data\": [ {"
" \"name\": \"vivso-suboptions\","
" \"data\": 4491"
" \"data\": \"4491\""
"},"
"{"
" \"name\": \"tftp-servers\","
@@ -221,7 +221,7 @@ const char* HOST_CONFIGS[] = {
" \"ip-address\": \"10.0.0.7\","
" \"option-data\": [ {"
" \"name\": \"vivso-suboptions\","
" \"data\": 4491"
" \"data\": \"4491\""
" },"
" {"
" \"name\": \"tftp-servers\","

View File

@@ -29,6 +29,7 @@
#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>
@@ -184,10 +185,12 @@ public:
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 if (entry == "user-context") {
user_context_ = param.second;
continue; // no parser to remember, simply store the value
@@ -425,8 +428,7 @@ protected:
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 {
@@ -703,11 +705,9 @@ DhcpConfigParser* createGlobal6DhcpConfigParser(const std::string& config_id,
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) {
@@ -808,7 +808,6 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
// 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;
@@ -838,10 +837,42 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
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)
@@ -850,9 +881,9 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
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
@@ -878,15 +909,6 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
}
}
// 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");

View File

@@ -2024,10 +2024,12 @@ TEST_F(Dhcpv6SrvTest, rsooOverride) {
" \"option-def\": [ {"
" \"name\": \"foo\","
" \"code\": 120,"
" \"csv-format\": false,"
" \"type\": \"binary\""
" } ],"
" \"option-data\": [ {"
" \"code\": 120,"
" \"csv-format\": false,"
" \"data\": \"05\""
" } ],"
" \"preferred-lifetime\": 3000,"

View File

@@ -229,7 +229,7 @@ const char* CONFIGS[] = {
"\"renew-timer\": 1000, "
"\"option-data\": [ {"
" \"name\": \"vendor-opts\","
" \"data\": 4491"
" \"data\": \"4491\""
"},"
"{"
" \"name\": \"tftp-servers\","
@@ -247,7 +247,7 @@ const char* CONFIGS[] = {
" \"ip-addresses\": [ \"2001:db8:1::2\" ],"
" \"option-data\": [ {"
" \"name\": \"vendor-opts\","
" \"data\": 4491"
" \"data\": \"4491\""
" },"
" {"
" \"name\": \"tftp-servers\","

View File

@@ -32,6 +32,8 @@ EXTRA_DIST =
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
@@ -161,6 +163,8 @@ libkea_dhcpsrv_la_SOURCES += parsers/dbaccess_parser.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

View File

@@ -91,12 +91,16 @@ ClientClassDefParser::build(ConstElementPtr class_def_cfg) {
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;

View File

@@ -376,75 +376,30 @@ HooksLibrariesParser::getLibraries(isc::hooks::HookLibsCollection& libraries,
}
// **************************** 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
@@ -454,14 +409,14 @@ OptionDataParser::extractCode(ConstElementPtr parent) const {
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 &&
@@ -469,7 +424,7 @@ OptionDataParser::extractCode(ConstElementPtr parent) const {
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)
<< ")");
}
@@ -481,7 +436,7 @@ OptionalValue<std::string>
OptionDataParser::extractName(ConstElementPtr parent) const {
std::string name;
try {
name = string_values_->getParam("name");
name = getString(parent, "name");
} catch (...) {
return (OptionalValue<std::string>());
@@ -490,17 +445,17 @@ OptionDataParser::extractName(ConstElementPtr parent) const {
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.
@@ -511,10 +466,10 @@ OptionDataParser::extractData() const {
}
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));
@@ -524,11 +479,11 @@ OptionDataParser::extractCSVFormat() const {
}
std::string
OptionDataParser::extractSpace() const {
OptionDataParser::extractSpace(ConstElementPtr parent) const {
std::string space = address_family_ == AF_INET ?
DHCP4_OPTION_SPACE : DHCP6_OPTION_SPACE;
try {
space = string_values_->getParam("space");
space = getString(parent, "space");
} catch (...) {
return (space);
@@ -556,7 +511,7 @@ OptionDataParser::extractSpace() const {
// 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);
@@ -588,16 +543,16 @@ OptionDataParser::findOptionDefinition(const std::string& option_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()) {
@@ -622,7 +577,7 @@ OptionDataParser::createOption(ConstElementPtr option_data) {
<< 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
@@ -631,7 +586,7 @@ OptionDataParser::createOption(ConstElementPtr option_data) {
isc_throw(DhcpConfigError, "definition for the option '"
<< space_param << "." << name_param
<< "' does not exist ("
<< string_values_->getPosition("name", option_data)
<< getPosition("name", option_data)
<< ")");
}
}
@@ -664,12 +619,14 @@ OptionDataParser::createOption(ConstElementPtr 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
@@ -678,13 +635,9 @@ OptionDataParser::createOption(ConstElementPtr option_data) {
// 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.
@@ -693,7 +646,7 @@ OptionDataParser::createOption(ConstElementPtr option_data) {
<< name_param << "' does not match the "
<< "option definition: '" << space_param
<< "." << def->getName() << "' ("
<< string_values_->getPosition("name", option_data)
<< getPosition("name", option_data)
<< ")");
}
@@ -704,144 +657,65 @@ OptionDataParser::createOption(ConstElementPtr 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_OPTION_SPACE :
DHCP6_OPTION_SPACE);
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.
@@ -854,14 +728,14 @@ OptionDefParser::createOptionDef(ConstElementPtr option_def_element) {
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,
@@ -888,7 +762,7 @@ OptionDefParser::createOptionDef(ConstElementPtr option_def_element) {
isc_throw(DhcpConfigError, "invalid record type values"
<< " specified for the option definition: "
<< ex.what() << " ("
<< string_values_->getPosition("record-types") << ")");
<< getPosition("record-types", option_def) << ")");
}
}
@@ -897,38 +771,43 @@ OptionDefParser::createOptionDef(ConstElementPtr option_def_element) {
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;
void
OptionDefListParser::commit() {
// Do nothing.
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() << ")");
}
}
// All definitions have been prepared. Put them as runtime options into
// the libdhcp++.
LibDHCP::setRuntimeOptionDefs(storage->getContainer());
}
//****************************** RelayInfoParser ********************************
@@ -1138,13 +1017,9 @@ PoolParser::build(ConstElementPtr pool_structure) {
" 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() << ")");
@@ -1194,6 +1069,13 @@ SubnetConfigParser::build(ConstElementPtr subnet) {
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

View File

@@ -15,7 +15,9 @@
#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>
@@ -534,39 +536,30 @@ private:
/// 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
@@ -594,22 +587,19 @@ private:
/// 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.
@@ -619,8 +609,6 @@ private:
/// @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.
@@ -630,13 +618,14 @@ private:
/// @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.
///
@@ -644,27 +633,10 @@ private:
/// '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_;
@@ -680,97 +652,52 @@ typedef OptionDataParser *OptionDataParserFactory(const std::string&,
/// 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.
/// 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.
///
/// @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 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.
@@ -779,7 +706,7 @@ private:
/// 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.
///
@@ -787,29 +714,24 @@ public:
/// 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

View File

@@ -196,8 +196,12 @@ HostReservationParser4::build(isc::data::ConstElementPtr reservation_data) {
// 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.
@@ -255,8 +259,12 @@ HostReservationParser6::build(isc::data::ConstElementPtr reservation_data) {
// 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,

View File

@@ -0,0 +1,253 @@
#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

View File

@@ -0,0 +1,189 @@
#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

View File

@@ -16,6 +16,7 @@
#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>
@@ -292,7 +293,7 @@ public:
/// @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) {
@@ -310,6 +311,16 @@ public:
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
@@ -322,12 +333,27 @@ public:
}
}
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.
@@ -359,17 +385,9 @@ public:
/// @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);
@@ -392,13 +410,15 @@ public:
///
/// @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();
@@ -566,7 +586,7 @@ TEST_F(ParseConfigTest, defaultSpaceOptionDefTest) {
"}";
// Verify that the configuration string parses.
int rcode = parseConfiguration(config);
int rcode = parseConfiguration(config, true);
ASSERT_EQ(0, rcode);
@@ -828,7 +848,7 @@ TEST_F(ParseConfigTest, optionDataCSVFormatNoOptionDef) {
" } ]"
"}";
int rcode = 0;
ASSERT_NO_THROW(rcode = parseConfiguration(config));
ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
EXPECT_NE(0, rcode);
CfgMgr::instance().clear();
@@ -844,7 +864,7 @@ TEST_F(ParseConfigTest, optionDataCSVFormatNoOptionDef) {
" \"data\": \"0\""
" } ]"
"}";
ASSERT_NO_THROW(rcode = parseConfiguration(config));
ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
EXPECT_NE(0, rcode);
CfgMgr::instance().clear();
@@ -859,7 +879,7 @@ TEST_F(ParseConfigTest, optionDataCSVFormatNoOptionDef) {
" \"data\": \"0\""
" } ]"
"}";
ASSERT_NO_THROW(rcode = parseConfiguration(config));
ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
ASSERT_EQ(0, rcode);
OptionPtr opt = getOptionPtr(DHCP6_OPTION_SPACE, 25000);
ASSERT_TRUE(opt);
@@ -875,10 +895,11 @@ TEST_F(ParseConfigTest, optionDataCSVFormatNoOptionDef) {
" \"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_OPTION_SPACE, 25000);
ASSERT_TRUE(opt);
@@ -899,7 +920,7 @@ TEST_F(ParseConfigTest, optionDataNoName) {
" } ]"
"}";
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_OPTION_SPACE, 23));
@@ -919,7 +940,7 @@ TEST_F(ParseConfigTest, optionDataNoCode) {
" } ]"
"}";
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_OPTION_SPACE, 23));
@@ -938,7 +959,7 @@ TEST_F(ParseConfigTest, optionDataMinimal) {
" } ]"
"}";
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_OPTION_SPACE, 23));
@@ -955,7 +976,7 @@ TEST_F(ParseConfigTest, optionDataMinimal) {
" } ]"
"}";
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_OPTION_SPACE,
23));
@@ -984,7 +1005,7 @@ TEST_F(ParseConfigTest, optionDataMinimalWithOptionDef) {
"}";
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_OPTION_SPACE, 2345));
@@ -1010,7 +1031,7 @@ TEST_F(ParseConfigTest, optionDataMinimalWithOptionDef) {
"}";
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_OPTION_SPACE,
2345));
@@ -1031,7 +1052,7 @@ TEST_F(ParseConfigTest, emptyOptionData) {
"}";
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_OPTION_SPACE, D6O_DHCPV4_O_DHCPV6_SERVER));