namespace flex_option {
FlexOptionImpl::OptionConfig::OptionConfig(uint16_t code)
- : code_(code), action_(NONE) {
+ : code_(code), action_(NONE), csv_format_(true) {
}
FlexOptionImpl::OptionConfig::~OptionConfig() {
}
ConstElementPtr code_elem = option->get("code");
ConstElementPtr name_elem = option->get("name");
+ ConstElementPtr csv_format_elem = option->get("csv-format");
if (!code_elem && !name_elem) {
isc_throw(BadValue, "'code' or 'name' must be specified: "
<< option->str());
isc_throw(BadValue, "option " << code << " was already specified");
}
+ bool csv_format = true;
+ if (csv_format_elem) {
+ if (csv_format_elem->getType() != Element::boolean) {
+ isc_throw(BadValue, "'csv-format' must be a boolean: "
+ << csv_format_elem->str());
+ }
+ csv_format = csv_format_elem->boolValue();
+ }
+
Option::Universe universe;
if (family == AF_INET) {
universe = Option::V4;
}
OptionConfigPtr opt_cfg(new OptionConfig(code));
+
+ if (csv_format_elem) {
+ opt_cfg->setCSVFormat(csv_format);
+ }
+
// opt_cfg initial action is NONE.
parseAction(option, opt_cfg, universe,
"add", ADD, EvalContext::PARSER_STRING);
if (opt_cfg->getAction() == NONE) {
isc_throw(BadValue, "no action: " << option->str());
}
+
option_config_map_[code] = opt_cfg;
}
#include <dhcp/std_option_defs.h>
#include <eval/evaluate.h>
#include <eval/token.h>
+#include <util/strutil.h>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
return (expr_);
}
+ /// @brief Set csv format.
+ ///
+ /// @param flag the csv format.
+ void setCSVFormat(bool flag) {
+ csv_format_ = flag;
+ }
+
+ /// @brief Return csv format.
+ ///
+ /// @return the csv format.
+ bool getCSVFormat() const {
+ return (csv_format_);
+ }
+
private:
/// @brief The code.
uint16_t code_;
/// @brief The match expression.
isc::dhcp::ExpressionPtr expr_;
+
+ /// @brief The csv format flag of the option.
+ bool csv_format_;
};
/// @brief The type of shared pointers to option config.
break;
}
- def = isc::dhcp::LibDHCP::getOptionDef(space, opt_cfg->getCode());
+ if (opt_cfg->getCSVFormat()) {
+ def = isc::dhcp::LibDHCP::getOptionDef(space, opt_cfg->getCode());
- if (!def) {
- def = isc::dhcp::LibDHCP::getRuntimeOptionDef(space, opt_cfg->getCode());
- }
+ if (!def) {
+ def = isc::dhcp::LibDHCP::getRuntimeOptionDef(space, opt_cfg->getCode());
+ }
- if (!def) {
- def = isc::dhcp::LibDHCP::getLastResortOptionDef(space, opt_cfg->getCode());
- }
+ if (!def) {
+ def = isc::dhcp::LibDHCP::getLastResortOptionDef(space, opt_cfg->getCode());
+ }
- if (!def) {
+ if (!def) {
+ buffer.assign(value.begin(), value.end());
+ opt.reset(new isc::dhcp::Option(universe, opt_cfg->getCode(),
+ buffer));
+ } else {
+ std::vector<std::string> split_vec =
+ isc::util::str::tokens(value, ",", true);
+ opt = def->optionFactory(universe, opt_cfg->getCode(),
+ split_vec);
+ }
+ } else {
buffer.assign(value.begin(), value.end());
opt.reset(new isc::dhcp::Option(universe, opt_cfg->getCode(),
buffer));
- } else {
- std::vector<std::string> split_vec;
- if (def->getType() != isc::dhcp::OPT_STRING_TYPE) {
- boost::split(split_vec, value, boost::is_any_of(","));
- } else {
- split_vec.push_back(value);
- }
- opt = def->optionFactory(universe, opt_cfg->getCode(),
- split_vec);
}
// Add the option.
opt = response->getOption(opt_cfg->getCode());
}
- def = isc::dhcp::LibDHCP::getOptionDef(space, opt_cfg->getCode());
+ if (opt_cfg->getCSVFormat()) {
+ def = isc::dhcp::LibDHCP::getOptionDef(space, opt_cfg->getCode());
- if (!def) {
- def = isc::dhcp::LibDHCP::getRuntimeOptionDef(space, opt_cfg->getCode());
- }
+ if (!def) {
+ def = isc::dhcp::LibDHCP::getRuntimeOptionDef(space, opt_cfg->getCode());
+ }
- if (!def) {
- def = isc::dhcp::LibDHCP::getLastResortOptionDef(space, opt_cfg->getCode());
- }
+ if (!def) {
+ def = isc::dhcp::LibDHCP::getLastResortOptionDef(space, opt_cfg->getCode());
+ }
- if (!def) {
+ if (!def) {
+ buffer.assign(value.begin(), value.end());
+ opt.reset(new isc::dhcp::Option(universe, opt_cfg->getCode(),
+ buffer));
+ } else {
+ std::vector<std::string> split_vec =
+ isc::util::str::tokens(value, ",", true);
+ opt = def->optionFactory(universe, opt_cfg->getCode(),
+ split_vec);
+ }
+ } else {
buffer.assign(value.begin(), value.end());
opt.reset(new isc::dhcp::Option(universe, opt_cfg->getCode(),
buffer));
- } else {
- std::vector<std::string> split_vec;
- if (def->getType() != isc::dhcp::OPT_STRING_TYPE) {
- boost::split(split_vec, value, boost::is_any_of(","));
- } else {
- split_vec.push_back(value);
- }
- opt = def->optionFactory(universe, opt_cfg->getCode(),
- split_vec);
}
// Add the option.
EXPECT_EQ(expected, impl_->getErrMsg());
}
+// Verify that the csv-format must be a boolean.
+TEST_F(FlexOptionTest, optionConfigBadCSVFormat) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr csv_format = Element::create(123);
+ option->set("csv-format", csv_format);
+ ElementPtr code = Element::create(12);
+ option->set("code", code);
+ EXPECT_THROW(impl_->testConfigure(options), BadValue);
+ EXPECT_EQ("'csv-format' must be a boolean: 123", impl_->getErrMsg());
+}
+
// Verify that an option can be configured only once.
TEST_F(FlexOptionTest, optionConfigTwice) {
ElementPtr options = Element::createList();
EXPECT_EQ(0, buffer_fqdn[12]);
}
+// Verify that ADD action adds the specified option in binary format.
+TEST_F(FlexOptionTest, processAddDisableCSVFormat) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr csv_format = Element::create(false);
+ ElementPtr code = Element::create(DHO_HOST_NAME);
+ option->set("code", code);
+ ElementPtr add = Element::create(string("'abc'"));
+ option->set("add", add);
+ option->set("csv-format", csv_format);
+
+ option = Element::createMap();
+ options->add(option);
+ code = Element::create(DHO_DOMAIN_SEARCH);
+ option->set("code", code);
+ add = Element::create(string("0x076578616d706c6503636f6d00"));
+ option->set("add", add);
+ option->set("csv-format", csv_format);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty());
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ EXPECT_FALSE(response->getOption(DHO_HOST_NAME));
+ EXPECT_FALSE(response->getOption(DHO_DOMAIN_SEARCH));
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ OptionPtr opt = response->getOption(DHO_HOST_NAME);
+ ASSERT_TRUE(opt);
+ EXPECT_EQ(DHO_HOST_NAME, opt->getType());
+ const OptionBuffer& buffer = opt->getData();
+ ASSERT_EQ(3, buffer.size());
+ EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3));
+
+ opt = response->getOption(DHO_DOMAIN_SEARCH);
+ ASSERT_TRUE(opt);
+ EXPECT_EQ(DHO_DOMAIN_SEARCH, opt->getType());
+ const OptionBuffer& buffer_fqdn = opt->getData();
+ ASSERT_EQ(13, buffer_fqdn.size());
+ EXPECT_EQ(7, buffer_fqdn[0]);
+ EXPECT_EQ(0, memcmp(&buffer_fqdn[1], "example", 7));
+ EXPECT_EQ(3, buffer_fqdn[8]);
+ EXPECT_EQ(0, memcmp(&buffer_fqdn[9], "com", 3));
+ EXPECT_EQ(0, buffer_fqdn[12]);
+}
+
// Verify that ADD action does not add an already existing option.
TEST_F(FlexOptionTest, processAddExisting) {
CfgMgr::instance().setFamily(AF_INET6);
EXPECT_EQ(0, buffer_fqdn[12]);
}
+// Verify that SUPERSEDE action supersedes the specified option in binary format.
+TEST_F(FlexOptionTest, processSupersedeDisableCSVFormat) {
+ ElementPtr options = Element::createList();
+ ElementPtr option = Element::createMap();
+ options->add(option);
+ ElementPtr csv_format = Element::create(false);
+ ElementPtr code = Element::create(DHO_HOST_NAME);
+ option->set("code", code);
+ ElementPtr supersede = Element::create(string("'abc'"));
+ option->set("supersede", supersede);
+ option->set("csv-format", csv_format);
+
+ option = Element::createMap();
+ options->add(option);
+ code = Element::create(DHO_DOMAIN_SEARCH);
+ option->set("code", code);
+ supersede = Element::create(string("0x076578616d706c6503636f6d00"));
+ option->set("supersede", supersede);
+ option->set("csv-format", csv_format);
+
+ EXPECT_NO_THROW(impl_->testConfigure(options));
+ EXPECT_TRUE(impl_->getErrMsg().empty());
+
+ Pkt4Ptr query(new Pkt4(DHCPDISCOVER, 12345));
+ Pkt4Ptr response(new Pkt4(DHCPOFFER, 12345));
+ EXPECT_FALSE(response->getOption(DHO_HOST_NAME));
+ EXPECT_FALSE(response->getOption(DHO_DOMAIN_SEARCH));
+
+ EXPECT_NO_THROW(impl_->process<Pkt4Ptr>(Option::V4, query, response));
+
+ OptionPtr opt = response->getOption(DHO_HOST_NAME);
+ ASSERT_TRUE(opt);
+ EXPECT_EQ(DHO_HOST_NAME, opt->getType());
+ const OptionBuffer& buffer = opt->getData();
+ ASSERT_EQ(3, buffer.size());
+ EXPECT_EQ(0, memcmp(&buffer[0], "abc", 3));
+
+ opt = response->getOption(DHO_DOMAIN_SEARCH);
+ ASSERT_TRUE(opt);
+ EXPECT_EQ(DHO_DOMAIN_SEARCH, opt->getType());
+ const OptionBuffer& buffer_fqdn = opt->getData();
+ ASSERT_EQ(13, buffer_fqdn.size());
+ EXPECT_EQ(7, buffer_fqdn[0]);
+ EXPECT_EQ(0, memcmp(&buffer_fqdn[1], "example", 7));
+ EXPECT_EQ(3, buffer_fqdn[8]);
+ EXPECT_EQ(0, memcmp(&buffer_fqdn[9], "com", 3));
+ EXPECT_EQ(0, buffer_fqdn[12]);
+}
+
// Verify that SUPERSEDE action supersedes an already existing option.
TEST_F(FlexOptionTest, processSupersedeExisting) {
CfgMgr::instance().setFamily(AF_INET6);
ASSERT_TRUE(opt);
EXPECT_EQ(D6O_NEW_POSIX_TIMEZONE, opt->getType());
const OptionBuffer& buffer = opt->getData();
- EXPECT_EQ(37, buffer.size());
- std::string data("EST5EDT4\\,M3.2.0/02:00\\,M11.1.0/02:00");
+ EXPECT_EQ(35, buffer.size());
+ std::string data("EST5EDT4,M3.2.0/02:00,M11.1.0/02:00");
EXPECT_EQ(0, memcmp(&buffer[0], &data[0], buffer.size()));
}
ASSERT_TRUE(opt);
EXPECT_EQ(D6O_NEW_POSIX_TIMEZONE, opt->getType());
const OptionBuffer& buffer = opt->getData();
- EXPECT_EQ(37, buffer.size());
- std::string data("EST5EDT4\\,M3.2.0/02:00\\,M11.1.0/02:00");
+ EXPECT_EQ(35, buffer.size());
+ std::string data("EST5EDT4,M3.2.0/02:00,M11.1.0/02:00");
EXPECT_EQ(0, memcmp(&buffer[0], &data[0], buffer.size()));
}