Commit e39b6108 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[2317] Created common function to get configured parameter from a storage.

parent 533c4afd
......@@ -37,6 +37,17 @@ using namespace isc::asiolink;
namespace {
// Forward declarations of some of the parser classes.
// They are used to define pointer types for these classes.
class BooleanParser;
class StringParser;
class Uint32Parser;
// Pointers to various parser objects.
typedef boost::shared_ptr<BooleanParser> BooleanParserPtr;
typedef boost::shared_ptr<StringParser> StringParserPtr;
typedef boost::shared_ptr<Uint32Parser> Uint32ParserPtr;
/// @brief auxiliary type used for storing element name and its parser
typedef pair<string, ConstElementPtr> ConfigPair;
......@@ -149,7 +160,7 @@ public:
value_(false) {
// Empty parameter name is invalid.
if (param_name_.empty()) {
isc_throw(isc::dhcp::Dhcp4ConfigError, "parser logic error:"
isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
<< "empty parameter name provided");
}
}
......@@ -160,7 +171,7 @@ public:
///
/// @throw isc::InvalidOperation if a storage has not been set
/// prior to calling this function
/// @throw isc::dhcp::Dhcp4ConfigError if a provided parameter's
/// @throw isc::dhcp::DhcpConfigError if a provided parameter's
/// name is empty.
virtual void build(ConstElementPtr value) {
if (storage_ == NULL) {
......@@ -168,7 +179,7 @@ public:
<< " storage for the " << param_name_
<< " value has not been set");
} else if (param_name_.empty()) {
isc_throw(isc::dhcp::Dhcp4ConfigError, "parser logic error:"
isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
<< "empty parameter name provided");
}
// The Config Manager checks if user specified a
......@@ -233,7 +244,7 @@ public:
param_name_(param_name) {
// Empty parameter name is invalid.
if (param_name_.empty()) {
isc_throw(Dhcp4ConfigError, "parser logic error:"
isc_throw(DhcpConfigError, "parser logic error:"
<< "empty parameter name provided");
}
}
......@@ -245,7 +256,7 @@ public:
/// or the parameter name is empty.
virtual void build(ConstElementPtr value) {
if (param_name_.empty()) {
isc_throw(Dhcp4ConfigError, "parser logic error:"
isc_throw(DhcpConfigError, "parser logic error:"
<< "empty parameter name provided");
}
......@@ -326,7 +337,7 @@ public:
:storage_(&string_defaults), param_name_(param_name) {
// Empty parameter name is invalid.
if (param_name_.empty()) {
isc_throw(Dhcp4ConfigError, "parser logic error:"
isc_throw(DhcpConfigError, "parser logic error:"
<< "empty parameter name provided");
}
}
......@@ -460,7 +471,7 @@ public:
/// interface name.
/// @param pools_list list of pools defined for a subnet
/// @throw InvalidOperation if storage was not specified (setStorage() not called)
/// @throw Dhcp4ConfigError when pool parsing fails
/// @throw DhcpConfigError when pool parsing fails
void build(ConstElementPtr pools_list) {
// setStorage() should have been called before build
if (!pools_) {
......@@ -500,7 +511,7 @@ public:
// be checked in Pool4 constructor.
len = boost::lexical_cast<int>(prefix_len);
} catch (...) {
isc_throw(Dhcp4ConfigError, "Failed to parse pool "
isc_throw(DhcpConfigError, "Failed to parse pool "
"definition: " << text_pool->stringValue());
}
......@@ -522,7 +533,7 @@ public:
continue;
}
isc_throw(Dhcp4ConfigError, "Failed to parse pool definition:"
isc_throw(DhcpConfigError, "Failed to parse pool definition:"
<< text_pool->stringValue() <<
". Does not contain - (for min-max) nor / (prefix/len)");
}
......@@ -603,7 +614,7 @@ public:
///
/// @param option_data_entries collection of entries that define value
/// for a particular option.
/// @throw Dhcp4ConfigError if invalid parameter specified in
/// @throw DhcpConfigError if invalid parameter specified in
/// the configuration.
/// @throw isc::InvalidOperation if failed to set storage prior to
/// calling build.
......@@ -644,7 +655,7 @@ public:
parser = value_parser;
}
} else {
isc_throw(Dhcp4ConfigError,
isc_throw(DhcpConfigError,
"Parser error: option-data parameter not supported: "
<< param.first);
}
......@@ -718,36 +729,36 @@ private:
/// is intitialized but this check is not needed here because it is done
/// in the \ref build function.
///
/// @throw Dhcp4ConfigError if parameters provided in the configuration
/// @throw DhcpConfigError if parameters provided in the configuration
/// are invalid.
void createOption() {
// Option code is held in the uint32_t storage but is supposed to
// be uint16_t value. We need to check that value in the configuration
// does not exceed range of uint16_t and is not zero.
uint32_t option_code = getUint32Param("code");
uint32_t option_code = getParam<uint32_t>("code", uint32_values_);
if (option_code == 0) {
isc_throw(Dhcp4ConfigError, "Parser error: value of 'code' must not"
isc_throw(DhcpConfigError, "Parser error: value of 'code' must not"
<< " be equal to zero. Option code '0' is reserved in"
<< " DHCPv4.");
} else if (option_code > std::numeric_limits<uint16_t>::max()) {
isc_throw(Dhcp4ConfigError, "Parser error: value of 'code' must not"
isc_throw(DhcpConfigError, "Parser error: value of 'code' must not"
<< " exceed " << std::numeric_limits<uint16_t>::max());
}
// Check that the option name has been specified, is non-empty and does not
// contain spaces.
// @todo possibly some more restrictions apply here?
std::string option_name = getStringParam("name");
std::string option_name = getParam<std::string>("name", string_values_);
if (option_name.empty()) {
isc_throw(Dhcp4ConfigError, "Parser error: option name must not be"
isc_throw(DhcpConfigError, "Parser error: option name must not be"
<< " empty");
} else if (option_name.find(" ") != std::string::npos) {
isc_throw(Dhcp4ConfigError, "Parser error: option name must not contain"
isc_throw(DhcpConfigError, "Parser error: option name must not contain"
<< " spaces");
}
// Get option data from the configuration database ('data' field).
const std::string option_data = getStringParam("data");
const bool csv_format = getBooleanParam("csv-format");
const std::string option_data = getParam<std::string>("data", string_values_);
const bool csv_format = getParam<bool>("csv-format", boolean_values_);
// Transform string of hexadecimal digits into binary format.
std::vector<uint8_t> binary;
std::vector<std::string> data_tokens;
......@@ -764,7 +775,7 @@ private:
try {
util::encode::decodeHex(option_data, binary);
} catch (...) {
isc_throw(Dhcp4ConfigError, "Parser error: option data is not a valid"
isc_throw(DhcpConfigError, "Parser error: option data is not a valid"
<< " string of hexadecimal digits: " << option_data);
}
}
......@@ -781,13 +792,13 @@ private:
// Currently we do not allow duplicated definitions and if there are
// any duplicates we issue internal server error.
if (num_defs > 1) {
isc_throw(Dhcp4ConfigError, "Internal error: currently it is not"
isc_throw(DhcpConfigError, "Internal error: currently it is not"
<< " supported to initialize multiple option definitions"
<< " for the same option code. This will be supported once"
<< " there option spaces are implemented.");
} else if (num_defs == 0) {
if (csv_format) {
isc_throw(Dhcp4ConfigError, "the CSV option data format can be"
isc_throw(DhcpConfigError, "the CSV option data format can be"
" used to specify values for an option that has a"
" definition. The option with code " << option_code
<< " does not have a definition.");
......@@ -817,54 +828,13 @@ private:
option_descriptor_.option = option;
option_descriptor_.persistent = false;
} catch (const isc::Exception& ex) {
isc_throw(Dhcp4ConfigError, "Parser error: option data does not match"
isc_throw(DhcpConfigError, "Parser error: option data does not match"
<< " option definition (code " << option_code << "): "
<< ex.what());
}
}
}
/// @brief Get a parameter from the strings storage.
///
/// @param param_id parameter identifier.
/// @throw Dhcp4ConfigError if parameter has not been found.
std::string getStringParam(const std::string& param_id) const {
StringStorage::const_iterator param = string_values_.find(param_id);
if (param == string_values_.end()) {
isc_throw(Dhcp4ConfigError, "Parser error: option-data parameter"
<< " '" << param_id << "' not specified");
}
return (param->second);
}
/// @brief Get a parameter from the uint32 values storage.
///
/// @param param_id parameter identifier.
/// @throw Dhcp4ConfigError if parameter has not been found.
uint32_t getUint32Param(const std::string& param_id) const {
Uint32Storage::const_iterator param = uint32_values_.find(param_id);
if (param == uint32_values_.end()) {
isc_throw(Dhcp4ConfigError, "Parser error: option-data parameter"
<< " '" << param_id << "' not specified");
}
return (param->second);
}
/// @brief Get a parameter from the boolean values storage.
///
/// @param param_id parameter identifier.
///
/// @throw isc::dhcp::Dhcp6ConfigError if a parameter has not been found.
/// @return a value of the boolean parameter.
bool getBooleanParam(const std::string& param_id) const {
BooleanStorage::const_iterator param = boolean_values_.find(param_id);
if (param == boolean_values_.end()) {
isc_throw(isc::dhcp::Dhcp4ConfigError, "parser error: option-data parameter"
<< " '" << param_id << "' not specified");
}
return (param->second);
}
/// Storage for uint32 values (e.g. option code).
Uint32Storage uint32_values_;
/// Storage for string values (e.g. option name or data).
......@@ -901,7 +871,7 @@ public:
/// for options within a single subnet and creates options' instances.
///
/// @param option_data_list pointer to a list of options' data sets.
/// @throw Dhcp4ConfigError if option parsing failed.
/// @throw DhcpConfigError if option parsing failed.
void build(ConstElementPtr option_data_list) {
BOOST_FOREACH(ConstElementPtr option_value, option_data_list->listValue()) {
boost::shared_ptr<OptionDataParser> parser(new OptionDataParser("option-data"));
......@@ -975,12 +945,53 @@ public:
///
/// @param option_def a configuration entry to be parsed.
///
/// @throw Dhcp4ConfigError if parsing was unsuccessful.
/// @throw DhcpConfigError if parsing was unsuccessful.
void build(ConstElementPtr option_def) {
if (storage_ == NULL) {
isc_throw(DhcpConfigError, "parser logic error: storage must be set"
" before parsing option definition data");
}
// 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 == "space") {
StringParserPtr
str_parser(dynamic_cast<StringParser*>(StringParser::factory(entry)));
if (str_parser) {
str_parser->setStorage(&string_values_);
parser = str_parser;
}
} else if (entry == "code") {
Uint32ParserPtr
code_parser(dynamic_cast<Uint32Parser*>(Uint32Parser::factory(entry)));
if (code_parser) {
code_parser->setStorage(&uint32_values_);
parser = code_parser;
}
} else if (entry == "array") {
BooleanParserPtr
array_parser(dynamic_cast<BooleanParser*>(BooleanParser::factory(entry)));
if (array_parser) {
array_parser->setStorage(&boolean_values_);
parser = array_parser;
}
} else if (entry == "record_types") {
// do nothing yet
} else {
isc_throw(DhcpConfigError, "invalid parameter: " << entry);
}
parser->build(param.second);
parser->commit();
}
createOptionDef();
}
/// @brief Stores the parsed option definition in a storage.
void commit() {
}
/// @brief Sets a pointer to a storage.
......@@ -996,12 +1007,27 @@ public:
private:
void createOptionDef() {
std::string name = getParam<std::string>("name", string_values_);
uint32_t code = getParam<uint32_t>("code", uint32_values_);
std::string type = getParam<std::string>("type", string_values_);
bool array_type = getParam<bool>("array", boolean_values_);
std::string space = getParam<std::string>("space", string_values_);
}
/// Instance of option definition being created by this parser.
OptionDefinitionPtr option_definition_;
/// Pointer to a storage where the option definition will be
/// added when \ref commit is called.
OptionDefContainer* storage_;
/// Storage for boolean values.
BooleanStorage boolean_values_;
/// Storage for string values.
StringStorage string_values_;
/// Storage for uint32 values.
Uint32Storage uint32_values_;
};
/// @brief Parser for a list of option definitions.
......@@ -1029,13 +1055,13 @@ public:
///
/// @param option_def_list pointer to an element that holds entries
/// that define option definitions.
/// @throw Dhcp4ConfigError if configuration parsing fails.
/// @throw DhcpConfigError if configuration parsing fails.
void build(ConstElementPtr option_def_list) {
if (!option_def_list) {
isc_throw(Dhcp4ConfigError, "parser error: a pointer to a list of"
isc_throw(DhcpConfigError, "parser error: a pointer to a list of"
<< " option definitions is NULL");
} else if (option_defs_ == NULL) {
isc_throw(Dhcp4ConfigError, "parser error: the storage for option"
isc_throw(DhcpConfigError, "parser error: the storage for option"
<< " definitions must be set before parsing option"
<< " definitions");
}
......@@ -1121,7 +1147,7 @@ public:
// Appropriate parsers are created in the createSubnet6ConfigParser
// and they should be limited to those that we check here for. Thus,
// if we fail to find a matching parser here it is a programming error.
isc_throw(Dhcp4ConfigError, "failed to find suitable parser");
isc_throw(DhcpConfigError, "failed to find suitable parser");
}
}
// In order to create new subnet we need to get the data out
......@@ -1146,7 +1172,7 @@ public:
/// storing the values that are actually consumed here. Pool definitions
/// created in other parsers are used here and added to newly created Subnet4
/// objects. Subnet4 are then added to DHCP CfgMgr.
/// @throw Dhcp4ConfigError if there are any issues encountered during commit
/// @throw DhcpConfigError if there are any issues encountered during commit
void commit() {
if (subnet_) {
CfgMgr::instance().addSubnet4(subnet_);
......@@ -1191,11 +1217,11 @@ private:
/// @brief Create a new subnet using a data from child parsers.
///
/// @throw isc::dhcp::Dhcp4ConfigError if subnet configuration parsing failed.
/// @throw isc::dhcp::DhcpConfigError if subnet configuration parsing failed.
void createSubnet() {
StringStorage::const_iterator it = string_values_.find("subnet");
if (it == string_values_.end()) {
isc_throw(Dhcp4ConfigError,
isc_throw(DhcpConfigError,
"Mandatory subnet definition in subnet missing");
}
// Remove any spaces or tabs.
......@@ -1210,7 +1236,7 @@ private:
// need to get all characters preceding "/".
size_t pos = subnet_txt.find("/");
if (pos == string::npos) {
isc_throw(Dhcp4ConfigError,
isc_throw(DhcpConfigError,
"Invalid subnet syntax (prefix/len expected):" << it->second);
}
......@@ -1315,7 +1341,7 @@ private:
///
/// @param name name of the parameter
/// @return triplet with the parameter name
/// @throw Dhcp4ConfigError when requested parameter is not present
/// @throw DhcpConfigError when requested parameter is not present
Triplet<uint32_t> getParam(const std::string& name) {
uint32_t value = 0;
bool found = false;
......@@ -1334,7 +1360,7 @@ private:
if (found) {
return (Triplet<uint32_t>(value));
} else {
isc_throw(Dhcp4ConfigError, "Mandatory parameter " << name
isc_throw(DhcpConfigError, "Mandatory parameter " << name
<< " missing (no global default and no subnet-"
<< "specific value)");
}
......
......@@ -28,20 +28,6 @@ namespace dhcp {
class Dhcpv4Srv;
/// An exception that is thrown if an error occurs while configuring an
/// @c Dhcpv4Srv object.
class Dhcp4ConfigError : public isc::Exception {
public:
/// @brief constructor
///
/// @param file name of the file, where exception occurred
/// @param line line of the file, where exception occurred
/// @param what text description of the issue that caused exception
Dhcp4ConfigError(const char* file, size_t line, const char* what)
: isc::Exception(file, line, what) {}
};
/// @brief Configure DHCPv4 server (@c Dhcpv4Srv) with a set of configuration values.
///
/// This function parses configuration information stored in @c config_set
......
......@@ -19,6 +19,7 @@
#include <cc/session.h>
#include <config/ccsession.h>
#include <dhcp/iface_mgr.h>
#include <dhcpsrv/dhcp_config_parser.h>
#include <dhcp4/ctrl_dhcp4_srv.h>
#include <dhcp4/dhcp4_log.h>
#include <dhcp4/spec_config.h>
......@@ -121,7 +122,7 @@ void ControlledDhcpv4Srv::establishSession() {
try {
configureDhcp4Server(*this, config_session_->getFullConfig());
} catch (const Dhcp4ConfigError& ex) {
} catch (const DhcpConfigError& ex) {
LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL).arg(ex.what());
}
......
......@@ -161,7 +161,7 @@ public:
value_(false) {
// Empty parameter name is invalid.
if (param_name_.empty()) {
isc_throw(isc::dhcp::Dhcp6ConfigError, "parser logic error:"
isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
<< "empty parameter name provided");
}
}
......@@ -172,7 +172,7 @@ public:
///
/// @throw isc::InvalidOperation if a storage has not been set
/// prior to calling this function
/// @throw isc::dhcp::Dhcp6ConfigError if a provided parameter's
/// @throw isc::dhcp::DhcpConfigError if a provided parameter's
/// name is empty.
virtual void build(ConstElementPtr value) {
if (storage_ == NULL) {
......@@ -180,7 +180,7 @@ public:
<< " storage for the " << param_name_
<< " value has not been set");
} else if (param_name_.empty()) {
isc_throw(isc::dhcp::Dhcp6ConfigError, "parser logic error:"
isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
<< "empty parameter name provided");
}
// The Config Manager checks if user specified a
......@@ -249,7 +249,7 @@ public:
param_name_(param_name) {
// Empty parameter name is invalid.
if (param_name_.empty()) {
isc_throw(Dhcp6ConfigError, "parser logic error:"
isc_throw(DhcpConfigError, "parser logic error:"
<< "empty parameter name provided");
}
}
......@@ -257,11 +257,11 @@ public:
/// @brief Parses configuration configuration parameter as uint32_t.
///
/// @param value pointer to the content of parsed values
/// @throw isc::dhcp::Dhcp6ConfigError if failed to parse
/// @throw isc::dhcp::DhcpConfigError if failed to parse
/// the configuration parameter as uint32_t value.
virtual void build(ConstElementPtr value) {
if (param_name_.empty()) {
isc_throw(isc::dhcp::Dhcp6ConfigError, "parser logic error:"
isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
<< "empty parameter name provided");
}
......@@ -291,7 +291,7 @@ public:
}
// Invalid value provided.
if (parse_error) {
isc_throw(isc::dhcp::Dhcp6ConfigError, "Failed to parse value " << value->str()
isc_throw(isc::dhcp::DhcpConfigError, "Failed to parse value " << value->str()
<< " as unsigned 32-bit integer.");
}
}
......@@ -352,7 +352,7 @@ public:
param_name_(param_name) {
// Empty parameter name is invalid.
if (param_name_.empty()) {
isc_throw(Dhcp6ConfigError, "parser logic error:"
isc_throw(DhcpConfigError, "parser logic error:"
<< "empty parameter name provided");
}
}
......@@ -362,10 +362,10 @@ public:
/// Parses configuration parameter's value as string.
///
/// @param value pointer to the content of parsed values
/// @throws Dhcp6ConfigError if the parsed parameter's name is empty.
/// @throws DhcpConfigError if the parsed parameter's name is empty.
virtual void build(ConstElementPtr value) {
if (param_name_.empty()) {
isc_throw(isc::dhcp::Dhcp6ConfigError, "parser logic error:"
isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
<< "empty parameter name provided");
}
value_ = value->str();
......@@ -528,7 +528,7 @@ public:
// be checked in Pool6 constructor.
len = boost::lexical_cast<int>(prefix_len);
} catch (...) {
isc_throw(Dhcp6ConfigError, "failed to parse pool "
isc_throw(DhcpConfigError, "failed to parse pool "
"definition: " << text_pool->stringValue());
}
......@@ -550,7 +550,7 @@ public:
continue;
}
isc_throw(Dhcp6ConfigError, "failed to parse pool definition:"
isc_throw(DhcpConfigError, "failed to parse pool definition:"
<< text_pool->stringValue() <<
". Does not contain - (for min-max) nor / (prefix/len)");
}
......@@ -632,7 +632,7 @@ public:
///
/// @param option_data_entries collection of entries that define value
/// for a particular option.
/// @throw Dhcp6ConfigError if invalid parameter specified in
/// @throw DhcpConfigError if invalid parameter specified in
/// the configuration.
/// @throw isc::InvalidOperation if failed to set storage prior to
/// calling build.
......@@ -674,7 +674,7 @@ public:
parser = value_parser;
}
} else {
isc_throw(Dhcp6ConfigError,
isc_throw(DhcpConfigError,
"parser error: option-data parameter not supported: "
<< param.first);
}
......@@ -748,37 +748,37 @@ private:
/// is intitialized but this check is not needed here because it is done
/// in the \ref build function.
///
/// @throw Dhcp6ConfigError if parameters provided in the configuration
/// @throw DhcpConfigError if parameters provided in the configuration
/// are invalid.
void createOption() {
// Option code is held in the uint32_t storage but is supposed to
// be uint16_t value. We need to check that value in the configuration
// does not exceed range of uint16_t and is not zero.
uint32_t option_code = getUint32Param("code");
uint32_t option_code = getParam<uint32_t>("code", uint32_values_);
if (option_code == 0) {
isc_throw(Dhcp6ConfigError, "Parser error: value of 'code' must not"
isc_throw(DhcpConfigError, "Parser error: value of 'code' must not"
<< " be equal to zero. Option code '0' is reserved in"
<< " DHCPv6.");
} else if (option_code > std::numeric_limits<uint16_t>::max()) {
isc_throw(Dhcp6ConfigError, "Parser error: value of 'code' must not"
isc_throw(DhcpConfigError, "Parser error: value of 'code' must not"
<< " exceed " << std::numeric_limits<uint16_t>::max());
}
// Check that the option name has been specified, is non-empty and does not
// contain spaces.
// @todo possibly some more restrictions apply here?
std::string option_name = getStringParam("name");
std::string option_name = getParam<std::string>("name", string_values_);
if (option_name.empty()) {
isc_throw(Dhcp6ConfigError, "Parser error: option name must not be"
isc_throw(DhcpConfigError, "Parser error: option name must not be"
<< " empty");
} else if (option_name.find(" ") != std::string::npos) {
isc_throw(Dhcp6ConfigError, "Parser error: option name must not contain"
isc_throw(DhcpConfigError, "Parser error: option name must not contain"
<< " spaces");
}
// Get option data from the configuration database ('data' field).
const std::string option_data = getStringParam("data");
const bool csv_format = getBooleanParam("csv-format");
const std::string option_data = getParam<std::string>("data", string_values_);
const bool csv_format = getParam<bool>("csv-format", boolean_values_);
std::vector<uint8_t> binary;
std::vector<std::string> data_tokens;
if (csv_format) {
......@@ -793,7 +793,7 @@ private:
try {
isc::util::encode::decodeHex(option_data, binary);
} catch (...) {
isc_throw(Dhcp6ConfigError, "Parser error: option data is not a valid"
isc_throw(DhcpConfigError, "Parser error: option data is not a valid"
<< " string of hexadecimal digits: " << option_data);
}
}
......@@ -810,13 +810,13 @@ private:
// Currently we do not allow duplicated definitions and if there are
// any duplicates we issue internal server error.
if (num_defs > 1) {
isc_throw(Dhcp6ConfigError, "Internal error: currently it is not"
isc_throw(DhcpConfigError, "Internal error: currently it is not"
<< " supported to initialize multiple option definitions"
<< " for the same option code. This will be supported once"
<< " there option spaces are implemented.");
} else if (num_defs == 0) {
if (csv_format) {
isc_throw(Dhcp6ConfigError, "the CSV option data format can be"
isc_throw(DhcpConfigError, "the CSV option data format can be"
" used to specify values for an option that has a"
" definition. The option with code " << option_code
<< " does not have a definition.");
......@@ -845,58 +845,13 @@ private:
option_descriptor_.option = option;
option_descriptor_.persistent = false;