2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-30 21:45:37 +00:00

[4204fd] Merge original trac4204

This commit is contained in:
Francis Dupont
2015-11-26 13:04:34 +01:00
38 changed files with 1318 additions and 332 deletions

View File

@@ -22,7 +22,6 @@
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/parsers/client_class_def_parser.h>
#include <dhcp4/json_config_parser.h>
#include <dhcpsrv/option_space_container.h>
#include <dhcpsrv/parsers/dbaccess_parser.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
#include <dhcpsrv/parsers/expiration_config_parser.h>
@@ -514,6 +513,12 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
// Remove any existing timers.
TimerMgr::instance()->unregisterTimers();
// Revert any runtime option definitions configured so far and not committed.
LibDHCP::revertRuntimeOptionDefs();
// Let's set empty container in case a user hasn't specified any configuration
// for option definitions. This is equivalent to commiting empty container.
LibDHCP::setRuntimeOptionDefs(OptionDefSpaceContainer());
// Some of the values specified in the configuration depend on
// other values. Typically, the values in the subnet4 structure
// depend on the global values. Also, option values configuration
@@ -700,6 +705,9 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
// Rollback changes as the configuration parsing failed.
if (rollback) {
globalContext().reset(new ParserContext(original_context));
// Revert to original configuration of runtime option definitions
// in the libdhcp++.
LibDHCP::revertRuntimeOptionDefs();
return (answer);
}

View File

@@ -1346,6 +1346,11 @@ TEST_F(Dhcp4ParserTest, optionDefIpv4Address) {
ASSERT_TRUE(status);
checkResult(status, 0);
// We need to commit option definitions because later in this test we
// will be checking if they get removed when "option-def" parameter
// is removed from a configuration.
LibDHCP::commitRuntimeOptionDefs();
// The option definition should now be available in the CfgMgr.
def = CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->get("isc", 100);
ASSERT_TRUE(def);
@@ -1356,6 +1361,25 @@ TEST_F(Dhcp4ParserTest, optionDefIpv4Address) {
EXPECT_FALSE(def->getArrayType());
EXPECT_EQ(OPT_IPV4_ADDRESS_TYPE, def->getType());
EXPECT_TRUE(def->getEncapsulatedSpace().empty());
// The copy of the option definition should be available in the libdhcp++.
OptionDefinitionPtr def_libdhcp = LibDHCP::getRuntimeOptionDef("isc", 100);
ASSERT_TRUE(def_libdhcp);
// Both definitions should be held in distinct pointers but they should
// be equal.
EXPECT_TRUE(def_libdhcp != def);
EXPECT_TRUE(*def_libdhcp == *def);
// Let's apply empty configuration. This removes the option definitions
// configuration and should result in removal of the option 100 from the
// libdhcp++.
config = "{ }";
json = Element::fromJSON(config);
ASSERT_NO_THROW(status = configureDhcp4Server(*srv_, json));
checkResult(status, 0);
EXPECT_FALSE(LibDHCP::getRuntimeOptionDef("isc", 100));
}
// The goal of this test is to check whether an option definition
@@ -1468,6 +1492,14 @@ TEST_F(Dhcp4ParserTest, optionDefMultiple) {
// The goal of this test is to verify that the duplicated option
// definition is not accepted.
TEST_F(Dhcp4ParserTest, optionDefDuplicate) {
// Preconfigure libdhcp++ with option definitions. The new configuration
// should override it, but when the new configuration fails, it should
// revert to this original configuration.
OptionDefSpaceContainer defs;
OptionDefinitionPtr def(new OptionDefinition("bar", 233, "string"));
defs.addItem(def, "isc");
LibDHCP::setRuntimeOptionDefs(defs);
LibDHCP::commitRuntimeOptionDefs();
// Configuration string. Both option definitions have
// the same code and belong to the same option space.
@@ -1498,6 +1530,15 @@ TEST_F(Dhcp4ParserTest, optionDefDuplicate) {
ASSERT_TRUE(status);
checkResult(status, 1);
EXPECT_TRUE(errorContainsPosition(status, "<string>"));
// The new configuration should have inserted option 100, but
// once configuration failed (on the duplicate option definition)
// the original configuration in libdhcp++ should be reverted.
EXPECT_FALSE(LibDHCP::getRuntimeOptionDef("isc", 100));
def = LibDHCP::getRuntimeOptionDef("isc", 233);
ASSERT_TRUE(def);
EXPECT_EQ("bar", def->getName());
EXPECT_EQ(233, def->getCode());
}
// The goal of this test is to verify that the option definition

View File

@@ -15,6 +15,7 @@
#include <config.h>
#include <cc/data.h>
#include <config/command_mgr.h>
#include <dhcp/libdhcp++.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcp6/ctrl_dhcp6_srv.h>
#include <dhcp6/dhcp6_log.h>
@@ -219,6 +220,10 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
}
}
// Finally, we can commit runtime option definitions in libdhcp++. This is
// exception free.
LibDHCP::commitRuntimeOptionDefs();
return (answer);
}

View File

@@ -752,6 +752,12 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
// Remove any existing timers.
TimerMgr::instance()->unregisterTimers();
// Revert any runtime option definitions configured so far and not committed.
LibDHCP::revertRuntimeOptionDefs();
// Let's set empty container in case a user hasn't specified any configuration
// for option definitions. This is equivalent to commiting empty container.
LibDHCP::setRuntimeOptionDefs(OptionDefSpaceContainer());
// Some of the values specified in the configuration depend on
// other values. Typically, the values in the subnet6 structure
// depend on the global values. Also, option values configuration
@@ -945,6 +951,9 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
// Rollback changes as the configuration parsing failed.
if (rollback) {
globalContext().reset(new ParserContext(original_context));
// Revert to original configuration of runtime option definitions
// in the libdhcp++.
LibDHCP::revertRuntimeOptionDefs();
return (answer);
}

View File

@@ -1583,6 +1583,12 @@ TEST_F(Dhcp6ParserTest, optionDefIpv6Address) {
ConstElementPtr status;
EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
ASSERT_TRUE(status);
checkResult(status, 0);
// We need to commit option definitions because later in this test we
// will be checking if they get removed when "option-def" parameter
// is removed from a configuration.
LibDHCP::commitRuntimeOptionDefs();
// The option definition should now be available in the CfgMgr.
def = CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->get("isc", 100);
@@ -1593,6 +1599,25 @@ TEST_F(Dhcp6ParserTest, optionDefIpv6Address) {
EXPECT_EQ(100, def->getCode());
EXPECT_FALSE(def->getArrayType());
EXPECT_EQ(OPT_IPV6_ADDRESS_TYPE, def->getType());
// The copy of the option definition should be available in the libdhcp++.
OptionDefinitionPtr def_libdhcp = LibDHCP::getRuntimeOptionDef("isc", 100);
ASSERT_TRUE(def_libdhcp);
// Both definitions should be held in distinct pointers but they should
// be equal.
EXPECT_TRUE(def_libdhcp != def);
EXPECT_TRUE(*def_libdhcp == *def);
// Let's apply empty configuration. This removes the option definitions
// configuration and should result in removal of the option 100 from the
// libdhcp++.
config = "{ }";
json = Element::fromJSON(config);
ASSERT_NO_THROW(status = configureDhcp6Server(srv_, json));
checkResult(status, 0);
EXPECT_FALSE(LibDHCP::getRuntimeOptionDef("isc", 100));
}
// The goal of this test is to check whether an option definition
@@ -1702,6 +1727,14 @@ TEST_F(Dhcp6ParserTest, optionDefMultiple) {
// The goal of this test is to verify that the duplicated option
// definition is not accepted.
TEST_F(Dhcp6ParserTest, optionDefDuplicate) {
// Preconfigure libdhcp++ with option definitions. The new configuration
// should override it, but when the new configuration fails, it should
// revert to this original configuration.
OptionDefSpaceContainer defs;
OptionDefinitionPtr def(new OptionDefinition("bar", 233, "string"));
defs.addItem(def, "isc");
LibDHCP::setRuntimeOptionDefs(defs);
LibDHCP::commitRuntimeOptionDefs();
// Configuration string. Both option definitions have
// the same code and belong to the same option space.
@@ -1732,6 +1765,15 @@ TEST_F(Dhcp6ParserTest, optionDefDuplicate) {
ASSERT_TRUE(status);
checkResult(status, 1);
EXPECT_TRUE(errorContainsPosition(status, "<string>"));
// The new configuration should have inserted option 100, but
// once configuration failed (on the duplicate option definition)
// the original configuration in libdhcp++ should be reverted.
EXPECT_FALSE(LibDHCP::getRuntimeOptionDef("isc", 100));
def = LibDHCP::getRuntimeOptionDef("isc", 233);
ASSERT_TRUE(def);
EXPECT_EQ("bar", def->getName());
EXPECT_EQ(233, def->getCode());
}
// The goal of this test is to verify that the option definition

View File

@@ -43,6 +43,7 @@ libkea_dhcp___la_SOURCES += option_data_types.cc option_data_types.h
libkea_dhcp___la_SOURCES += option_definition.cc option_definition.h
libkea_dhcp___la_SOURCES += option_opaque_data_tuples.cc option_opaque_data_tuples.h
libkea_dhcp___la_SOURCES += option_space.cc option_space.h
libkea_dhcp___la_SOURCES += option_space_container.h
libkea_dhcp___la_SOURCES += option_string.cc option_string.h
libkea_dhcp___la_SOURCES += protocol_util.cc protocol_util.h
libkea_dhcp___la_SOURCES += pkt.cc pkt.h

View File

@@ -32,6 +32,8 @@
#include <boost/shared_array.hpp>
#include <boost/shared_ptr.hpp>
#include <list>
using namespace std;
using namespace isc::dhcp;
using namespace isc::util;
@@ -52,6 +54,10 @@ VendorOptionDefContainers LibDHCP::vendor4_defs_;
VendorOptionDefContainers LibDHCP::vendor6_defs_;
// Static container with option definitions created in runtime.
StagedValue<OptionDefSpaceContainer> LibDHCP::runtime_option_defs_;
// Those two vendor classes are used for cable modems:
/// DOCSIS3.0 compatible cable modem
@@ -194,6 +200,66 @@ LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id,
return (OptionDefinitionPtr());
}
OptionDefinitionPtr
LibDHCP::getRuntimeOptionDef(const std::string& space, const uint16_t code) {
OptionDefContainerPtr container = runtime_option_defs_.getValue().getItems(space);
const OptionDefContainerTypeIndex& index = container->get<1>();
const OptionDefContainerTypeRange& range = index.equal_range(code);
if (range.first != range.second) {
return (*range.first);
}
return (OptionDefinitionPtr());
}
OptionDefinitionPtr
LibDHCP::getRuntimeOptionDef(const std::string& space, const std::string& name) {
OptionDefContainerPtr container = runtime_option_defs_.getValue().getItems(space);
const OptionDefContainerNameIndex& index = container->get<2>();
const OptionDefContainerNameRange& range = index.equal_range(name);
if (range.first != range.second) {
return (*range.first);
}
return (OptionDefinitionPtr());
}
OptionDefContainerPtr
LibDHCP::getRuntimeOptionDefs(const std::string& space) {
return (runtime_option_defs_.getValue().getItems(space));
}
void
LibDHCP::setRuntimeOptionDefs(const OptionDefSpaceContainer& defs) {
OptionDefSpaceContainer defs_copy;
std::list<std::string> option_space_names = defs.getOptionSpaceNames();
for (std::list<std::string>::const_iterator name = option_space_names.begin();
name != option_space_names.end(); ++name) {
OptionDefContainerPtr container = defs.getItems(*name);
for (OptionDefContainer::const_iterator def = container->begin();
def != container->end(); ++def) {
OptionDefinitionPtr def_copy(new OptionDefinition(**def));
defs_copy.addItem(def_copy, *name);
}
}
runtime_option_defs_ = defs_copy;
}
void
LibDHCP::clearRuntimeOptionDefs() {
runtime_option_defs_.reset();
}
void
LibDHCP::revertRuntimeOptionDefs() {
runtime_option_defs_.revert();
}
void
LibDHCP::commitRuntimeOptionDefs() {
runtime_option_defs_.commit();
}
bool
LibDHCP::isStandardOption(const Option::Universe u, const uint16_t code) {
if (u == Option::V6) {
@@ -260,7 +326,14 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
OptionDefContainer option_defs;
if (option_space == "dhcp6") {
option_defs = LibDHCP::getOptionDefs(Option::V6);
} else {
OptionDefContainerPtr option_defs_ptr =
LibDHCP::getRuntimeOptionDefs(option_space);
if (option_defs_ptr) {
option_defs = *option_defs_ptr;
}
}
// @todo Once we implement other option spaces we should add else clause
// here and gather option definitions for them. For now leaving option_defs
// empty will imply creation of generic Option.

View File

@@ -16,8 +16,10 @@
#define LIBDHCP_H
#include <dhcp/option_definition.h>
#include <dhcp/option_space_container.h>
#include <dhcp/pkt6.h>
#include <util/buffer.h>
#include <util/staged_value.h>
#include <iostream>
#include <string>
@@ -90,6 +92,36 @@ public:
const uint32_t vendor_id,
const std::string& name);
/// @brief Returns runtime (non-standard) option definition by space and
/// option code.
///
/// @param space Option space name.
/// @param code Option code.
///
/// @return Pointer to option definition or NULL if it doesn't exist.
static OptionDefinitionPtr getRuntimeOptionDef(const std::string& space,
const uint16_t code);
/// @brief Returns runtime (non-standard) option definition by space and
/// option name.
///
/// @param space Option space name.
/// @param name Option name.
///
/// @return Pointer to option definition or NULL if it doesn't exist.
static OptionDefinitionPtr getRuntimeOptionDef(const std::string& space,
const std::string& name);
/// @brief Returns runtime (non-standard) option definitions for specified
/// option space name.
///
/// @param space Option space name.
///
/// @return Pointer to the container holding option definitions or NULL.
static OptionDefContainerPtr
getRuntimeOptionDefs(const std::string& space);
/// @brief Check if the specified option is a standard option.
///
/// @param u universe (V4 or V6)
@@ -256,6 +288,27 @@ public:
const OptionBuffer& buf,
isc::dhcp::OptionCollection& options);
/// @brief Copies option definitions created at runtime.
///
/// Copied option definitions will be used as "runtime" option definitions.
/// A typical use case is to set option definitions specified by the user
/// in the server configuration. These option definitions should be removed
/// or replaced with new option definitions upon reconfiguration.
///
/// @param defs Const reference to a container holding option definitions
/// grouped by option spaces.
static void setRuntimeOptionDefs(const OptionDefSpaceContainer& defs);
/// @brief Removes runtime option definitions.
static void clearRuntimeOptionDefs();
/// @brief Reverts uncommited changes to runtime option definitions.
static void revertRuntimeOptionDefs();
/// @brief Commits runtime option definitions.
static void commitRuntimeOptionDefs();
private:
/// Initialize standard DHCPv4 option definitions.
@@ -301,6 +354,9 @@ private:
/// Container for v6 vendor option definitions
static VendorOptionDefContainers vendor6_defs_;
/// Container for additional option defnitions created in runtime.
static util::StagedValue<OptionDefSpaceContainer> runtime_option_defs_;
};
}

View File

@@ -220,8 +220,8 @@ Option::toString() {
return (toText(0));
}
std::string
Option::toHexString(const bool include_header) {
std::vector<uint8_t>
Option::toBinary(const bool include_header) {
OutputBuffer buf(len());
try {
// If the option is too long, exception will be thrown. We allow
@@ -233,12 +233,18 @@ Option::toHexString(const bool include_header) {
" of option " << getType() << ": " << ex.what());
}
const uint8_t* option_data = static_cast<const uint8_t*>(buf.getData());
std::vector<uint8_t> option_vec;
// Assign option data to a vector, with or without option header depending
// on the value of "include_header" flag.
option_vec.assign(option_data + (include_header ? 0 : getHeaderLen()),
option_data + buf.getLength());
std::vector<uint8_t> option_vec(option_data + (include_header ? 0 : getHeaderLen()),
option_data + buf.getLength());
return (option_vec);
}
std::string
Option::toHexString(const bool include_header) {
// Prepare binary version of the option.
std::vector<uint8_t> option_vec = toBinary(include_header);
// Return hexadecimal representation prepended with 0x or empty string
// if option has no payload and the header fields are excluded.

View File

@@ -216,6 +216,15 @@ public:
/// @return string that represents the value of the option.
virtual std::string toString();
/// @brief Returns binary representation of the option.
///
/// @param include_header Boolean flag which indicates if the output should
/// also contain header fields. The default is that it shouldn't include
/// header fields.
///
/// @return Vector holding binary representation of the option.
virtual std::vector<uint8_t> toBinary(const bool include_header = false);
/// @brief Returns string containing hexadecimal representation of option.
///
/// @param include_header Boolean flag which indicates if the output should

View File

@@ -17,6 +17,7 @@
#include <dhcp/option.h>
#include <dhcp/option_data_types.h>
#include <dhcp/option_space_container.h>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/mem_fun.hpp>
@@ -766,6 +767,10 @@ typedef OptionDefContainer::nth_index<2>::type OptionDefContainerNameIndex;
typedef std::pair<OptionDefContainerNameIndex::const_iterator,
OptionDefContainerNameIndex::const_iterator> OptionDefContainerNameRange;
typedef OptionSpaceContainer<
OptionDefContainer, OptionDefinitionPtr, std::string
> OptionDefSpaceContainer;
} // namespace isc::dhcp
} // namespace isc

View File

@@ -147,6 +147,60 @@ public:
return (OptionBuffer(opt_data, opt_data + sizeof(opt_data)));
}
/// @brief Create option definitions and store in the container.
///
/// @param spaces_num Number of option spaces to be created.
/// @param defs_num Number of option definitions to be created for
/// each option space.
/// @param [out] defs Container to which option definitions should be
/// added.
static void createRuntimeOptionDefs(const uint16_t spaces_num,
const uint16_t defs_num,
OptionDefSpaceContainer& defs) {
for (uint16_t space = 0; space < spaces_num; ++space) {
std::ostringstream space_name;
space_name << "option-space-" << space;
for (uint16_t code = 0; code < defs_num; ++code) {
std::ostringstream name;
name << "name-for-option-" << code;
OptionDefinitionPtr opt_def(new OptionDefinition(name.str(),
code, "string"));
defs.addItem(opt_def, space_name.str());
}
}
}
/// @brief Test if runtime option definitions have been added.
///
/// This method uses the same naming conventions for space names and
/// options names as @c createRuntimeOptionDefs method.
///
/// @param spaces_num Number of option spaces to be tested.
/// @param defs_num Number of option definitions that should exist
/// in each option space.
/// @param should_exist Boolean value which indicates if option
/// definitions should exist. If this is false, this function will
/// check that they don't exist.
static void testRuntimeOptionDefs(const uint16_t spaces_num,
const uint16_t defs_num,
const bool should_exist) {
for (uint16_t space = 0; space < spaces_num; ++space) {
std::ostringstream space_name;
space_name << "option-space-" << space;
for (uint16_t code = 0; code < defs_num; ++code) {
std::ostringstream name;
name << "name-for-option-" << code;
OptionDefinitionPtr opt_def =
LibDHCP::getRuntimeOptionDef(space_name.str(), name.str());
if (should_exist) {
ASSERT_TRUE(opt_def);
} else {
ASSERT_FALSE(opt_def);
}
}
}
}
private:
/// @brief Test DHCPv4 or DHCPv6 option definition.
@@ -1319,4 +1373,28 @@ TEST_F(LibDhcpTest, vendorClass6) {
EXPECT_EQ("eRouter1.0", vclass->getTuple(0).getText());
}
// This test verifies that it is possible to add runtime option definitions,
// retrieve them and remove them.
TEST_F(LibDhcpTest, setRuntimeOptionDefs) {
// Create option definitions in 5 namespaces.
OptionDefSpaceContainer defs;
createRuntimeOptionDefs(5, 100, defs);
// Apply option definitions.
ASSERT_NO_THROW(LibDHCP::setRuntimeOptionDefs(defs));
// Retrieve all inserted option definitions.
testRuntimeOptionDefs(5, 100, true);
// Attempting to retrieve non existing definitions.
EXPECT_FALSE(LibDHCP::getRuntimeOptionDef("option-space-non-existent", 1));
EXPECT_FALSE(LibDHCP::getRuntimeOptionDef("option-space-0", 145));
// Remove all runtime option definitions.
ASSERT_NO_THROW(LibDHCP::clearRuntimeOptionDefs());
// All option definitions should be gone now.
testRuntimeOptionDefs(5, 100, false);
}
} // end of anonymous space

View File

@@ -128,7 +128,6 @@ libkea_dhcpsrv_la_SOURCES += ncr_generator.cc ncr_generator.h
if HAVE_PGSQL
libkea_dhcpsrv_la_SOURCES += pgsql_lease_mgr.cc pgsql_lease_mgr.h
endif
libkea_dhcpsrv_la_SOURCES += option_space_container.h
libkea_dhcpsrv_la_SOURCES += pool.cc pool.h
libkea_dhcpsrv_la_SOURCES += srv_config.cc srv_config.h
libkea_dhcpsrv_la_SOURCES += subnet.cc subnet.h

View File

@@ -16,8 +16,8 @@
#define CFG_OPTION_H
#include <dhcp/option.h>
#include <dhcp/option_space_container.h>
#include <dhcpsrv/key_from_key.h>
#include <dhcpsrv/option_space_container.h>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/sequenced_index.hpp>

View File

@@ -16,7 +16,7 @@
#define CFG_OPTION_DEF_H
#include <dhcp/option_definition.h>
#include <dhcpsrv/option_space_container.h>
#include <dhcp/option_space_container.h>
#include <string>
namespace isc {
@@ -120,14 +120,18 @@ public:
OptionDefinitionPtr get(const std::string& option_space,
const std::string& option_name) const;
/// @brief Returns reference to container holding option definitions.
const OptionDefSpaceContainer& getContainer() const {
return (option_definitions_);
}
private:
/// @brief A collection of option definitions.
///
/// The option definitions stored in this container can be accessed
/// using the option space name they belong to.
OptionSpaceContainer<OptionDefContainer, OptionDefinitionPtr,
std::string> option_definitions_;
OptionDefSpaceContainer option_definitions_;
};

View File

@@ -50,7 +50,7 @@ ExpressionParser::build(ConstElementPtr expression_cfg) {
std::string value;
expression_cfg->getValue(value);
try {
EvalContext eval_ctx;
EvalContext eval_ctx(global_context_->universe_);
eval_ctx.parseString(value);
local_expression_.reset(new Expression());
*local_expression_ = eval_ctx.expression;

View File

@@ -786,6 +786,12 @@ OptionDefParser::build(ConstElementPtr option_def) {
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

View File

@@ -18,10 +18,10 @@
#include <asiolink/io_address.h>
#include <cc/data.h>
#include <dhcp/option_definition.h>
#include <dhcp/option_space_container.h>
#include <dhcpsrv/d2_client_cfg.h>
#include <dhcpsrv/cfg_iface.h>
#include <dhcpsrv/cfg_option.h>
#include <dhcpsrv/option_space_container.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/parsers/dhcp_config_parser.h>
#include <exceptions/exceptions.h>
@@ -36,12 +36,6 @@
namespace isc {
namespace dhcp {
/// @brief Storage for option definitions.
typedef OptionSpaceContainer<OptionDefContainer,
OptionDefinitionPtr, std::string> OptionDefStorage;
/// @brief Shared pointer to option definitions storage.
typedef boost::shared_ptr<OptionDefStorage> OptionDefStoragePtr;
/// Collection of containers holding option spaces. Each container within
/// a particular option space holds so-called option descriptors.
typedef OptionSpaceContainer<OptionContainer, OptionDescriptor,

View File

@@ -18,8 +18,8 @@
#include <asiolink/io_address.h>
#include <dhcp/option.h>
#include <dhcp/classify.h>
#include <dhcp/option_space_container.h>
#include <dhcpsrv/cfg_option.h>
#include <dhcpsrv/option_space_container.h>
#include <dhcpsrv/pool.h>
#include <dhcpsrv/triplet.h>
#include <dhcpsrv/lease.h>

View File

@@ -15,6 +15,8 @@
#include <config.h>
#include <cc/data.h>
#include <dhcp/libdhcp++.h>
#include <dhcp/option.h>
#include <dhcp/option_string.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/parsers/client_class_def_parser.h>
@@ -32,6 +34,61 @@ using namespace isc::dhcp;
namespace {
/// @brief Test fixture class for @c ExpressionParser.
class ExpressionParserTest : public ::testing::Test {
protected:
/// @brief Test that validate expression can be evaluated against v4 or
/// v6 packet.
///
/// Verifies that given a valid expression, the ExpressionParser
/// produces an Expression which can be evaluated against a v4 or v6
/// packet.
///
/// @param universe V4 or V6.
/// @param expression Textual representation of the expression to be
/// evaluated.
/// @param option_string String data to be placed in the hostname
/// option, being placed in the packet used for evaluation.
/// @tparam Type of the packet: @c Pkt4 or @c Pkt6.
template<typename PktType>
void testValidExpression(const Option::Universe& universe,
const std::string& expression,
const std::string& option_string) {
ParserContextPtr context(new ParserContext(universe));
ExpressionParserPtr parser;
ExpressionPtr parsed_expr;
// Turn config into elements. This may emit exceptions.
ElementPtr config_element = Element::fromJSON(expression);
// Create the parser.
ASSERT_NO_THROW(parser.reset(new ExpressionParser("", parsed_expr,
context)));
// Expression should parse and commit.
ASSERT_NO_THROW(parser->build(config_element));
ASSERT_NO_THROW(parser->commit());
// Parsed expression should exist.
ASSERT_TRUE(parsed_expr);
// Build a packet that will fail evaluation.
boost::shared_ptr<PktType> pkt(new PktType(universe == Option::V4 ?
DHCPDISCOVER : DHCPV6_SOLICIT,
123));
EXPECT_FALSE(evaluate(*parsed_expr, *pkt));
// Now add the option so it will pass. Use a standard option carrying a
// single string value, i.e. hostname for DHCPv4 and bootfile url for
// DHCPv6.
OptionPtr opt(new OptionString(universe, universe == Option::V4 ?
DHO_HOST_NAME : D6O_BOOTFILE_URL,
option_string));
pkt->addOption(opt);
EXPECT_TRUE(evaluate(*parsed_expr, *pkt));
}
};
/// @brief Test fixture class for @c ClientClassDefParser.
class ClientClassDefParserTest : public ::testing::Test {
protected:
@@ -113,69 +170,66 @@ protected:
// Verifies that given a valid expression, the ExpressionParser
// produces an Expression which can be evaluated against a v4 packet.
TEST(ExpressionParserTest, validExpression4) {
ParserContextPtr context(new ParserContext(Option::V4));
ExpressionParserPtr parser;
ExpressionPtr parsed_expr;
TEST_F(ExpressionParserTest, validExpression4) {
testValidExpression<Pkt4>(Option::V4, "\"option[12].text == 'hundred4'\"",
"hundred4");
}
// Turn config into elements. This may emit exceptions.
std::string cfg_txt = "\"option[100].text == 'hundred4'\"";
ElementPtr config_element = Element::fromJSON(cfg_txt);
// Verifies that the option name can be used in the evaluated expression.
TEST_F(ExpressionParserTest, validExpressionWithOptionName4) {
testValidExpression<Pkt4>(Option::V4,
"\"option[host-name].text == 'hundred4'\"",
"hundred4");
}
// Create the parser.
ASSERT_NO_THROW(parser.reset(new ExpressionParser("", parsed_expr,
context)));
// Expression should parse and commit.
ASSERT_NO_THROW(parser->build(config_element));
ASSERT_NO_THROW(parser->commit());
// Verifies that given a valid expression using .hex operator for option, the
// ExpressionParser produces an Expression which can be evaluated against
// a v4 packet.
TEST_F(ExpressionParserTest, validExpressionWithHex4) {
testValidExpression<Pkt4>(Option::V4, "\"option[12].hex == 0x68756E6472656434\"",
"hundred4");
}
// Parsed expression should exist.
ASSERT_TRUE(parsed_expr);
// Build a packet that will fail evaluation.
Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, 123));
EXPECT_FALSE(evaluate(*parsed_expr, *pkt4));
// Now add the option so it will pass.
OptionPtr opt(new OptionString(Option::V4, 100, "hundred4"));
pkt4->addOption(opt);
EXPECT_TRUE(evaluate(*parsed_expr, *pkt4));
// Verifies that the option name can be used together with .hex operator in
// the evaluated expression.
TEST_F(ExpressionParserTest, validExpressionWithOptionNameAndHex4) {
testValidExpression<Pkt6>(Option::V4,
"\"option[host-name].text == 0x68756E6472656434\"",
"hundred4");
}
// Verifies that given a valid expression, the ExpressionParser
// produces an Expression which can be evaluated against a v6 packet.
TEST(ExpressionParserTest, validExpression6) {
ParserContextPtr context(new ParserContext(Option::V6));
ExpressionParserPtr parser;
ExpressionPtr parsed_expr;
// Turn config into elements. This may emit exceptions.
std::string cfg_txt = "\"option[100].text == 'hundred6'\"";
ElementPtr config_element = Element::fromJSON(cfg_txt);
// Create the parser.
ASSERT_NO_THROW(parser.reset(new ExpressionParser("", parsed_expr,
context)));
// Expression should parse and commit.
ASSERT_NO_THROW(parser->build(config_element));
ASSERT_NO_THROW(parser->commit());
// Parsed expression should exist.
ASSERT_TRUE(parsed_expr);
// Build a packet that will fail evaluation.
Pkt6Ptr pkt6(new Pkt6(DHCPDISCOVER, 123));
EXPECT_FALSE(evaluate(*parsed_expr, *pkt6));
// Now add the option so it will pass.
OptionPtr opt(new OptionString(Option::V6, 100, "hundred6"));
pkt6->addOption(opt);
EXPECT_TRUE(evaluate(*parsed_expr, *pkt6));
TEST_F(ExpressionParserTest, validExpression6) {
testValidExpression<Pkt6>(Option::V6, "\"option[59].text == 'hundred6'\"",
"hundred6");
}
// Verifies that the option name can be used in the evaluated expression.
TEST_F(ExpressionParserTest, validExpressionWithOptionName6) {
testValidExpression<Pkt6>(Option::V6,
"\"option[bootfile-url].text == 'hundred6'\"",
"hundred6");
}
// Verifies that given a valid expression using .hex operator for option, the
// ExpressionParser produces an Expression which can be evaluated against
// a v6 packet.
TEST_F(ExpressionParserTest, validExpressionWithHex6) {
testValidExpression<Pkt6>(Option::V6, "\"option[59].hex == 0x68756E6472656436\"",
"hundred6");
}
// Verifies that the option name can be used together with .hex operator in
// the evaluated expression.
TEST_F(ExpressionParserTest, validExpressionWithOptionNameAndHex6) {
testValidExpression<Pkt6>(Option::V6,
"\"option[bootfile-url].text == 0x68756E6472656436\"",
"hundred6");
}
// Verifies that an the ExpressionParser only accepts StringElements.
TEST(ExpressionParserTest, invalidExpressionElement) {
TEST_F(ExpressionParserTest, invalidExpressionElement) {
ParserContextPtr context(new ParserContext(Option::V4));
ExpressionParserPtr parser;
ExpressionPtr parsed_expr;
@@ -196,7 +250,7 @@ TEST(ExpressionParserTest, invalidExpressionElement) {
// is not intended to be an exhaustive test or expression syntax.
// It is simply to ensure that if the parser fails, it does so
// Properly.
TEST(ExpressionParserTest, expressionSyntaxError) {
TEST_F(ExpressionParserTest, expressionSyntaxError) {
ParserContextPtr context(new ParserContext(Option::V4));
ExpressionParserPtr parser;
ExpressionPtr parsed_expr;

View File

@@ -495,6 +495,15 @@ TEST_F(ParseConfigTest, basicOptionDefTest) {
EXPECT_FALSE(def->getArrayType());
EXPECT_EQ(OPT_IPV4_ADDRESS_TYPE, def->getType());
EXPECT_TRUE(def->getEncapsulatedSpace().empty());
// Check if libdhcp++ runtime options have been updated.
OptionDefinitionPtr def_libdhcp = LibDHCP::getRuntimeOptionDef("isc", 100);
ASSERT_TRUE(def_libdhcp);
// The LibDHCP should return a separate instance of the option definition
// but the values should be equal.
EXPECT_TRUE(def_libdhcp != def);
EXPECT_TRUE(*def_libdhcp == *def);
}
/// @brief Check minimal parsing of option definitions.

View File

@@ -15,10 +15,12 @@
#include <eval/eval_context.h>
#include <eval/parser.h>
#include <exceptions/exceptions.h>
#include <dhcp/option.h>
#include <fstream>
EvalContext::EvalContext()
: trace_scanning_(false), trace_parsing_(false)
EvalContext::EvalContext(const Option::Universe& option_universe)
: trace_scanning_(false), trace_parsing_(false),
option_universe_(option_universe)
{
}
@@ -32,7 +34,7 @@ EvalContext::parseString(const std::string& str)
file_ = "<string>";
string_ = str;
scanStringBegin();
isc::eval::EvalParser parser(*this);
isc::eval::EvalParser parser(*this, option_universe_);
parser.set_debug_level(trace_parsing_);
int res = parser.parse();
scanStringEnd();

View File

@@ -42,7 +42,11 @@ class EvalContext
{
public:
/// @brief Default constructor.
EvalContext();
///
/// @param option_universe Option universe: DHCPv4 or DHCPv6. This is used
/// by the parser to determine which option definitions set should be used
/// to map option names to option codes.
EvalContext(const Option::Universe& option_universe);
/// @brief destructor
virtual ~EvalContext();
@@ -55,7 +59,7 @@ public:
/// @brief Method called after the last tokens are scanned from a string.
void scanStringEnd();
/// @brief Run the parser on the string specified.
///
/// @param str string to be written
@@ -87,7 +91,12 @@ public:
/// @brief Flag determing parser debugging.
bool trace_parsing_;
/// @brief Option universe: DHCPv4 or DHCPv6.
///
/// This is used by the parser to determine which option definitions
/// set should be used to map option name to option code.
Option::Universe option_universe_;
};
}; // end of isc::eval namespace

View File

@@ -460,8 +460,8 @@ static void yy_fatal_error (yyconst char msg[] );
(yy_c_buf_p) = yy_cp;
/* %% [4.0] data tables for the DFA and the user's section 1 definitions go here */
#define YY_NUM_RULES 19
#define YY_END_OF_BUFFER 20
#define YY_NUM_RULES 20
#define YY_END_OF_BUFFER 21
/* This struct is not used in this scanner,
but its presence is necessary. */
struct yy_trans_info
@@ -469,14 +469,27 @@ struct yy_trans_info
flex_int32_t yy_verify;
flex_int32_t yy_nxt;
};
static yyconst flex_int16_t yy_accept[52] =
static yyconst flex_int16_t yy_acclist[84] =
{ 0,
0, 0, 20, 18, 1, 2, 18, 13, 14, 17,
18, 12, 5, 5, 18, 15, 16, 18, 18, 18,
18, 18, 1, 2, 0, 3, 5, 0, 6, 0,
0, 0, 0, 0, 4, 11, 9, 0, 0, 0,
0, 0, 8, 0, 0, 7, 0, 0, 0, 10,
0
21, 19, 20, 1, 19, 20, 2, 20, 19, 20,
13, 19, 20, 14, 19, 20, 17, 19, 20, 19,
20, 12, 19, 20, 5, 19, 20, 5, 19, 20,
19, 20, 19, 20, 15, 19, 20, 16, 19, 20,
19, 20, 19, 20, 19, 20, 19, 20, 19, 20,
1, 2, 3, 5, 6,16402,16402,16402,16402,16402,
16402, 4, 8210, 11,16402, 9,16402,16402,16402,16402,
16402,16402, 8,16402,16402,16402, 7,16402,16402,16402,
16402, 10,16402
} ;
static yyconst flex_int16_t yy_accept[57] =
{ 0,
1, 1, 1, 2, 4, 7, 9, 11, 14, 17,
20, 22, 25, 28, 31, 33, 35, 38, 41, 43,
45, 47, 49, 51, 52, 53, 53, 54, 55, 55,
56, 57, 58, 59, 60, 61, 62, 63, 63, 64,
66, 68, 69, 70, 71, 72, 73, 75, 76, 77,
79, 80, 81, 82, 84, 84
} ;
static yyconst flex_int32_t yy_ec[256] =
@@ -488,13 +501,13 @@ static yyconst flex_int32_t yy_ec[256] =
6, 1, 1, 7, 8, 9, 1, 10, 11, 11,
11, 11, 11, 11, 11, 11, 11, 1, 1, 1,
12, 1, 1, 1, 13, 13, 13, 13, 13, 13,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 14, 1, 1,
15, 1, 16, 1, 1, 1, 17, 18, 13, 13,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 15, 14, 14,
16, 1, 17, 1, 18, 1, 19, 20, 13, 13,
19, 13, 20, 21, 22, 1, 1, 23, 1, 24,
25, 26, 1, 27, 28, 29, 30, 1, 1, 31,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
21, 13, 22, 23, 24, 14, 14, 25, 14, 26,
27, 28, 14, 29, 30, 31, 32, 14, 14, 33,
14, 14, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
@@ -511,88 +524,110 @@ static yyconst flex_int32_t yy_ec[256] =
1, 1, 1, 1, 1
} ;
static yyconst flex_int32_t yy_meta[32] =
static yyconst flex_int32_t yy_meta[34] =
{ 0,
1, 1, 2, 1, 1, 1, 1, 1, 1, 3,
3, 1, 3, 1, 1, 1, 3, 3, 3, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1
1, 1, 2, 1, 1, 1, 1, 3, 1, 4,
4, 1, 4, 3, 3, 1, 1, 3, 4, 4,
4, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3
} ;
static yyconst flex_int16_t yy_base[54] =
static yyconst flex_int16_t yy_base[59] =
{ 0,
0, 0, 72, 73, 69, 67, 65, 73, 73, 73,
22, 73, 24, 26, 56, 73, 73, 44, 47, 39,
34, 44, 60, 58, 56, 73, 29, 0, 73, 36,
26, 25, 35, 21, 0, 73, 73, 29, 22, 20,
23, 18, 73, 22, 18, 73, 22, 19, 22, 73,
73, 55, 38
0, 0, 121, 122, 118, 116, 110, 122, 122, 122,
24, 122, 26, 28, 98, 0, 122, 122, 77, 79,
67, 61, 63, 68, 50, 47, 122, 32, 0, 122,
38, 43, 44, 45, 46, 47, 0, 48, 122, 50,
52, 54, 55, 56, 72, 73, 77, 79, 80, 81,
84, 86, 89, 90, 122, 112, 114, 40
} ;
static yyconst flex_int16_t yy_def[54] =
static yyconst flex_int16_t yy_def[59] =
{ 0,
51, 1, 51, 51, 51, 51, 52, 51, 51, 51,
51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
51, 51, 51, 51, 52, 51, 51, 53, 51, 51,
51, 51, 51, 51, 53, 51, 51, 51, 51, 51,
51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
0, 51, 51
55, 1, 55, 55, 55, 55, 56, 55, 55, 55,
55, 55, 55, 55, 55, 57, 55, 55, 57, 57,
57, 57, 57, 55, 55, 56, 55, 55, 58, 55,
57, 57, 57, 57, 57, 57, 58, 55, 55, 57,
57, 57, 57, 57, 57, 57, 57, 57, 57, 57,
57, 57, 57, 57, 0, 55, 55, 55
} ;
static yyconst flex_int16_t yy_nxt[105] =
static yyconst flex_int16_t yy_nxt[156] =
{ 0,
4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
14, 15, 4, 4, 16, 17, 18, 4, 4, 4,
19, 4, 4, 4, 20, 4, 4, 21, 22, 4,
4, 27, 27, 27, 27, 27, 27, 28, 27, 27,
35, 50, 49, 48, 47, 46, 45, 44, 43, 42,
41, 40, 39, 38, 28, 25, 37, 25, 36, 26,
24, 23, 34, 33, 32, 31, 30, 29, 26, 24,
23, 51, 3, 51, 51, 51, 51, 51, 51, 51,
51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
14, 15, 16, 16, 16, 17, 18, 4, 19, 16,
16, 16, 20, 16, 16, 16, 21, 16, 16, 22,
23, 16, 16, 28, 28, 28, 28, 28, 28, 38,
29, 28, 28, 37, 38, 38, 38, 38, 38, 38,
27, 38, 25, 38, 39, 38, 38, 38, 29, 39,
39, 39, 39, 39, 39, 43, 39, 40, 39, 24,
39, 39, 39, 38, 38, 42, 41, 45, 38, 44,
38, 38, 38, 36, 46, 38, 47, 38, 39, 39,
38, 38, 35, 39, 34, 39, 39, 39, 48, 33,
51, 51, 51, 51
39, 32, 39, 49, 50, 39, 39, 52, 51, 30,
54, 53, 26, 27, 26, 26, 31, 31, 25, 24,
55, 3, 55, 55, 55, 55, 55, 55, 55, 55,
55, 55, 55, 55, 55, 55, 55, 55, 55, 55,
55, 55, 55, 55, 55, 55, 55, 55, 55, 55,
55, 55, 55, 55, 55
} ;
static yyconst flex_int16_t yy_chk[105] =
static yyconst flex_int16_t yy_chk[156] =
{ 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 11, 11, 13, 13, 14, 14, 13, 27, 27,
53, 49, 48, 47, 45, 44, 42, 41, 40, 39,
38, 34, 33, 32, 13, 52, 31, 52, 30, 25,
24, 23, 22, 21, 20, 19, 18, 15, 7, 6,
5, 3, 51, 51, 51, 51, 51, 51, 51, 51,
51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
1, 1, 1, 11, 11, 13, 13, 14, 14, 31,
13, 28, 28, 58, 32, 33, 34, 35, 36, 38,
26, 40, 25, 41, 31, 42, 43, 44, 13, 32,
33, 34, 35, 36, 38, 35, 40, 32, 41, 24,
42, 43, 44, 45, 46, 34, 33, 42, 47, 36,
48, 49, 50, 23, 43, 51, 44, 52, 45, 46,
53, 54, 22, 47, 21, 48, 49, 50, 45, 20,
51, 51, 51, 51
51, 19, 52, 46, 48, 53, 54, 51, 49, 15,
53, 52, 56, 7, 56, 56, 57, 57, 6, 5,
3, 55, 55, 55, 55, 55, 55, 55, 55, 55,
55, 55, 55, 55, 55, 55, 55, 55, 55, 55,
55, 55, 55, 55, 55, 55, 55, 55, 55, 55,
55, 55, 55, 55, 55
} ;
/* Table of booleans, true if rule could match eol. */
static yyconst flex_int32_t yy_rule_can_match_eol[20] =
static yyconst flex_int32_t yy_rule_can_match_eol[21] =
{ 0,
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
static yy_state_type yy_last_accepting_state;
static char *yy_last_accepting_cpos;
0, };
extern int yy_flex_debug;
int yy_flex_debug = 1;
static yyconst flex_int16_t yy_rule_linenum[19] =
static yyconst flex_int16_t yy_rule_linenum[20] =
{ 0,
83, 87, 93, 103, 109, 123, 124, 125, 126, 127,
128, 129, 130, 131, 132, 133, 134, 136
128, 129, 130, 131, 132, 133, 134, 136, 143
} ;
/* The intent behind this definition is that it'll catch
* any uses of REJECT which flex missed.
*/
#define REJECT reject_used_but_not_detected
static yy_state_type *yy_state_buf=0, *yy_state_ptr=0;
static char *yy_full_match;
static int yy_lp;
static int yy_looking_for_trail_begin = 0;
static int yy_full_lp;
static int *yy_full_state;
#define YY_TRAILING_MASK 0x2000
#define YY_TRAILING_HEAD_MASK 0x4000
#define REJECT \
{ \
*yy_cp = (yy_hold_char); /* undo effects of setting up yytext */ \
yy_cp = (yy_full_match); /* restore poss. backed-over text */ \
(yy_lp) = (yy_full_lp); /* restore orig. accepting pos. */ \
(yy_state_ptr) = (yy_full_state); /* restore orig. state */ \
yy_current_state = *(yy_state_ptr); /* restore curr. state */ \
++(yy_lp); \
goto find_rule; \
}
#define yymore() yymore_used_but_not_detected
#define YY_MORE_ADJ 0
#define YY_RESTORE_YY_MORE_OFFSET
@@ -653,7 +688,7 @@ static isc::eval::location loc;
// by moving it ahead by yyleng bytes. yyleng specifies the length of the
// currently matched token.
#define YY_USER_ACTION loc.columns(yyleng);
#line 657 "lexer.cc"
#line 692 "lexer.cc"
#define INITIAL 0
@@ -901,7 +936,7 @@ YY_DECL
loc.step();
#line 905 "lexer.cc"
#line 940 "lexer.cc"
if ( !(yy_init) )
{
@@ -911,6 +946,12 @@ YY_DECL
YY_USER_INIT;
#endif
/* Create the reject buffer large enough to save one state per allowed character. */
if ( ! (yy_state_buf) )
(yy_state_buf) = (yy_state_type *)yyalloc(YY_STATE_BUF_SIZE );
if ( ! (yy_state_buf) )
YY_FATAL_ERROR( "out of dynamic memory in yylex()" );
if ( ! (yy_start) )
(yy_start) = 1; /* first start state */
@@ -952,31 +993,66 @@ YY_DECL
/* %% [9.0] code to set up and find next match goes here */
yy_current_state = (yy_start);
(yy_state_ptr) = (yy_state_buf);
*(yy_state_ptr)++ = yy_current_state;
yy_match:
do
{
register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
if ( yy_accept[yy_current_state] )
{
(yy_last_accepting_state) = yy_current_state;
(yy_last_accepting_cpos) = yy_cp;
}
while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
{
yy_current_state = (int) yy_def[yy_current_state];
if ( yy_current_state >= 52 )
if ( yy_current_state >= 56 )
yy_c = yy_meta[(unsigned int) yy_c];
}
yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
*(yy_state_ptr)++ = yy_current_state;
++yy_cp;
}
while ( yy_current_state != 51 );
yy_cp = (yy_last_accepting_cpos);
yy_current_state = (yy_last_accepting_state);
while ( yy_current_state != 55 );
yy_find_action:
/* %% [10.0] code to find the action number goes here */
yy_act = yy_accept[yy_current_state];
yy_current_state = *--(yy_state_ptr);
(yy_lp) = yy_accept[yy_current_state];
goto find_rule; /* Shut up GCC warning -Wall */
find_rule: /* we branch to this label when backing up */
for ( ; ; ) /* until we find what rule we matched */
{
if ( (yy_lp) && (yy_lp) < yy_accept[yy_current_state + 1] )
{
yy_act = yy_acclist[(yy_lp)];
if ( yy_act & YY_TRAILING_HEAD_MASK ||
(yy_looking_for_trail_begin) )
{
if ( yy_act == (yy_looking_for_trail_begin) )
{
(yy_looking_for_trail_begin) = 0;
yy_act &= ~YY_TRAILING_HEAD_MASK;
break;
}
}
else if ( yy_act & YY_TRAILING_MASK )
{
(yy_looking_for_trail_begin) = yy_act & ~YY_TRAILING_MASK;
(yy_looking_for_trail_begin) |= YY_TRAILING_HEAD_MASK;
}
else
{
(yy_full_match) = yy_cp;
(yy_full_state) = (yy_state_ptr);
(yy_full_lp) = (yy_lp);
break;
}
++(yy_lp);
goto find_rule;
}
--yy_cp;
yy_current_state = *--(yy_state_ptr);
(yy_lp) = yy_accept[yy_current_state];
}
YY_DO_BEFORE_ACTION;
@@ -999,13 +1075,13 @@ do_action: /* This label is used only to access EOF actions. */
{
if ( yy_act == 0 )
fprintf( stderr, "--scanner backing up\n" );
else if ( yy_act < 19 )
else if ( yy_act < 20 )
fprintf( stderr, "--accepting rule at line %ld (\"%s\")\n",
(long)yy_rule_linenum[yy_act], yytext );
else if ( yy_act == 19 )
else if ( yy_act == 20 )
fprintf( stderr, "--accepting default rule (\"%s\")\n",
yytext );
else if ( yy_act == 20 )
else if ( yy_act == 21 )
fprintf( stderr, "--(end of buffer or a NUL)\n" );
else
fprintf( stderr, "--EOF (start condition %d)\n", YY_START );
@@ -1014,13 +1090,6 @@ do_action: /* This label is used only to access EOF actions. */
switch ( yy_act )
{ /* beginning of action switch */
/* %% [13.0] actions go here */
case 0: /* must back up */
/* undo the effects of YY_DO_BEFORE_ACTION */
*yy_cp = (yy_hold_char);
yy_cp = (yy_last_accepting_cpos);
yy_current_state = (yy_last_accepting_state);
goto yy_find_action;
case 1:
YY_RULE_SETUP
#line 83 "lexer.ll"
@@ -1141,18 +1210,28 @@ return isc::eval::EvalParser::make_COMA(loc);
case 18:
YY_RULE_SETUP
#line 136 "lexer.ll"
driver.error (loc, "Invalid character: " + std::string(yytext));
YY_BREAK
case YY_STATE_EOF(INITIAL):
#line 137 "lexer.ll"
return isc::eval::EvalParser::make_END(loc);
{
// This string specifies option name starting with a letter
// and further containing letters, digits, hyphens and
// underscores.
return isc::eval::EvalParser::make_OPTION_NAME(yytext, loc);
}
YY_BREAK
case 19:
YY_RULE_SETUP
#line 138 "lexer.ll"
#line 143 "lexer.ll"
driver.error (loc, "Invalid character: " + std::string(yytext));
YY_BREAK
case YY_STATE_EOF(INITIAL):
#line 144 "lexer.ll"
return isc::eval::EvalParser::make_END(loc);
YY_BREAK
case 20:
YY_RULE_SETUP
#line 145 "lexer.ll"
ECHO;
YY_BREAK
#line 1156 "lexer.cc"
#line 1235 "lexer.cc"
case YY_END_OF_BUFFER:
{
@@ -1218,8 +1297,7 @@ ECHO;
else
{
/* %% [14.0] code to do back-up for compressed tables and set up yy_cp goes here */
yy_cp = (yy_last_accepting_cpos);
yy_current_state = (yy_last_accepting_state);
yy_cp = (yy_c_buf_p);
goto yy_find_action;
}
}
@@ -1356,37 +1434,8 @@ static int yy_get_next_buffer (void)
while ( num_to_read <= 0 )
{ /* Not enough room in the buffer - grow it. */
/* just a shorter name for the current buffer */
YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
int yy_c_buf_p_offset =
(int) ((yy_c_buf_p) - b->yy_ch_buf);
if ( b->yy_is_our_buffer )
{
yy_size_t new_size = b->yy_buf_size * 2;
if ( new_size <= 0 )
b->yy_buf_size += b->yy_buf_size / 8;
else
b->yy_buf_size *= 2;
b->yy_ch_buf = (char *)
/* Include room in for 2 EOB chars. */
yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 );
}
else
/* Can't grow it, we don't own it. */
b->yy_ch_buf = 0;
if ( ! b->yy_ch_buf )
YY_FATAL_ERROR(
"fatal error - scanner input buffer overflow" );
(yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
number_to_move - 1;
YY_FATAL_ERROR(
"input buffer overflow, can't enlarge buffer because scanner uses REJECT" );
}
@@ -1452,22 +1501,21 @@ static int yy_get_next_buffer (void)
/* %% [15.0] code to get the start state into yy_current_state goes here */
yy_current_state = (yy_start);
(yy_state_ptr) = (yy_state_buf);
*(yy_state_ptr)++ = yy_current_state;
for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
{
/* %% [16.0] code to find the next state goes here */
register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
if ( yy_accept[yy_current_state] )
{
(yy_last_accepting_state) = yy_current_state;
(yy_last_accepting_cpos) = yy_cp;
}
while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
{
yy_current_state = (int) yy_def[yy_current_state];
if ( yy_current_state >= 52 )
if ( yy_current_state >= 56 )
yy_c = yy_meta[(unsigned int) yy_c];
}
yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
*(yy_state_ptr)++ = yy_current_state;
}
return yy_current_state;
@@ -1486,22 +1534,18 @@ static int yy_get_next_buffer (void)
{
register int yy_is_jam;
/* %% [17.0] code to find the next state, and perhaps do backing up, goes here */
register char *yy_cp = (yy_c_buf_p);
register YY_CHAR yy_c = 1;
if ( yy_accept[yy_current_state] )
{
(yy_last_accepting_state) = yy_current_state;
(yy_last_accepting_cpos) = yy_cp;
}
while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
{
yy_current_state = (int) yy_def[yy_current_state];
if ( yy_current_state >= 52 )
if ( yy_current_state >= 56 )
yy_c = yy_meta[(unsigned int) yy_c];
}
yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
yy_is_jam = (yy_current_state == 51);
yy_is_jam = (yy_current_state == 55);
if ( ! yy_is_jam )
*(yy_state_ptr)++ = yy_current_state;
return yy_is_jam ? 0 : yy_current_state;
}
@@ -2154,6 +2198,11 @@ static int yy_init_globals (void)
(yy_init) = 0;
(yy_start) = 0;
(yy_state_buf) = 0;
(yy_state_ptr) = 0;
(yy_full_match) = 0;
(yy_lp) = 0;
/* Defined in main.c */
#ifdef YY_STDINIT
yyin = stdin;
@@ -2186,6 +2235,9 @@ int yylex_destroy (void)
yyfree((yy_buffer_stack) );
(yy_buffer_stack) = NULL;
yyfree ( (yy_state_buf) );
(yy_state_buf) = NULL;
/* Reset the globals. This is important in a non-reentrant scanner so the next time
* yylex() is called, initialization will occur. */
yy_init_globals( );
@@ -2249,7 +2301,7 @@ void yyfree (void * ptr )
/* %ok-for-header */
#line 138 "lexer.ll"
#line 145 "lexer.ll"

View File

@@ -133,6 +133,13 @@ blank [ \t]
"]" return isc::eval::EvalParser::make_RBRACKET(loc);
"," return isc::eval::EvalParser::make_COMA(loc);
[A-Za-z][A-Za-z0-9_\-]+/{blank}*] {
// This string specifies option name starting with a letter
// and further containing letters, digits, hyphens and
// underscores.
return isc::eval::EvalParser::make_OPTION_NAME(yytext, loc);
}
. driver.error (loc, "Invalid character: " + std::string(yytext));
<<EOF>> return isc::eval::EvalParser::make_END(loc);
%%

View File

@@ -49,10 +49,10 @@
#line 51 "parser.cc" // lalr1.cc:412
// Unqualified %code blocks.
#line 39 "parser.yy" // lalr1.cc:413
#line 40 "parser.yy" // lalr1.cc:413
# include "eval_context.h"
#line 67 "parser.yy" // lalr1.cc:413
#line 73 "parser.yy" // lalr1.cc:413
namespace {
@@ -206,13 +206,14 @@ namespace isc { namespace eval {
/// Build a parser object.
EvalParser::EvalParser (EvalContext& ctx_yyarg)
EvalParser::EvalParser (EvalContext& ctx_yyarg, const Option::Universe& option_universe_yyarg)
:
#if YYDEBUG
yydebug_ (false),
yycdebug_ (&std::cerr),
#endif
ctx (ctx_yyarg)
ctx (ctx_yyarg),
option_universe (option_universe_yyarg)
{}
EvalParser::~EvalParser ()
@@ -280,7 +281,8 @@ namespace isc { namespace eval {
case 15: // "constant string"
case 16: // "integer"
case 17: // "constant hexstring"
case 18: // TOKEN
case 18: // "option name"
case 19: // TOKEN
value.move< std::string > (that.value);
break;
@@ -302,7 +304,8 @@ namespace isc { namespace eval {
case 15: // "constant string"
case 16: // "integer"
case 17: // "constant hexstring"
case 18: // TOKEN
case 18: // "option name"
case 19: // TOKEN
value.copy< std::string > (that.value);
break;
@@ -344,30 +347,37 @@ namespace isc { namespace eval {
{
case 15: // "constant string"
#line 64 "parser.yy" // lalr1.cc:636
#line 70 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< std::string > (); }
#line 350 "parser.cc" // lalr1.cc:636
#line 353 "parser.cc" // lalr1.cc:636
break;
case 16: // "integer"
#line 64 "parser.yy" // lalr1.cc:636
#line 70 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< std::string > (); }
#line 357 "parser.cc" // lalr1.cc:636
#line 360 "parser.cc" // lalr1.cc:636
break;
case 17: // "constant hexstring"
#line 64 "parser.yy" // lalr1.cc:636
#line 70 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< std::string > (); }
#line 364 "parser.cc" // lalr1.cc:636
#line 367 "parser.cc" // lalr1.cc:636
break;
case 18: // TOKEN
case 18: // "option name"
#line 64 "parser.yy" // lalr1.cc:636
#line 70 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< std::string > (); }
#line 371 "parser.cc" // lalr1.cc:636
#line 374 "parser.cc" // lalr1.cc:636
break;
case 19: // TOKEN
#line 70 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< std::string > (); }
#line 381 "parser.cc" // lalr1.cc:636
break;
@@ -570,7 +580,8 @@ namespace isc { namespace eval {
case 15: // "constant string"
case 16: // "integer"
case 17: // "constant hexstring"
case 18: // TOKEN
case 18: // "option name"
case 19: // TOKEN
yylhs.value.build< std::string > ();
break;
@@ -592,90 +603,124 @@ namespace isc { namespace eval {
switch (yyn)
{
case 3:
#line 105 "parser.yy" // lalr1.cc:859
#line 111 "parser.yy" // lalr1.cc:859
{
TokenPtr eq(new TokenEqual());
ctx.expression.push_back(eq);
}
#line 601 "parser.cc" // lalr1.cc:859
#line 612 "parser.cc" // lalr1.cc:859
break;
case 4:
#line 112 "parser.yy" // lalr1.cc:859
#line 118 "parser.yy" // lalr1.cc:859
{
TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
ctx.expression.push_back(str);
}
#line 610 "parser.cc" // lalr1.cc:859
#line 621 "parser.cc" // lalr1.cc:859
break;
case 5:
#line 117 "parser.yy" // lalr1.cc:859
#line 123 "parser.yy" // lalr1.cc:859
{
TokenPtr hex(new TokenHexString(yystack_[0].value.as< std::string > ()));
ctx.expression.push_back(hex);
}
#line 619 "parser.cc" // lalr1.cc:859
#line 630 "parser.cc" // lalr1.cc:859
break;
case 6:
#line 122 "parser.yy" // lalr1.cc:859
#line 128 "parser.yy" // lalr1.cc:859
{
uint16_t numeric_code = convert_option_code(yystack_[3].value.as< std::string > (), yystack_[3].location, ctx);
TokenPtr opt(new TokenOption(numeric_code, TokenOption::TEXTUAL));
ctx.expression.push_back(opt);
}
#line 629 "parser.cc" // lalr1.cc:859
#line 640 "parser.cc" // lalr1.cc:859
break;
case 7:
#line 128 "parser.yy" // lalr1.cc:859
#line 134 "parser.yy" // lalr1.cc:859
{
uint16_t numeric_code = convert_option_code(yystack_[3].value.as< std::string > (), yystack_[3].location, ctx);
TokenPtr opt(new TokenOption(numeric_code, TokenOption::HEXADECIMAL));
ctx.expression.push_back(opt);
}
#line 639 "parser.cc" // lalr1.cc:859
#line 650 "parser.cc" // lalr1.cc:859
break;
case 8:
#line 134 "parser.yy" // lalr1.cc:859
#line 140 "parser.yy" // lalr1.cc:859
{
try {
// This may result in exception if the specified
// name is unknown.
TokenPtr opt(new TokenOption(yystack_[3].value.as< std::string > (), option_universe,
TokenOption::TEXTUAL));
ctx.expression.push_back(opt);
} catch (const std::exception& ex) {
ctx.error(yystack_[3].location, ex.what());
}
}
#line 667 "parser.cc" // lalr1.cc:859
break;
case 9:
#line 153 "parser.yy" // lalr1.cc:859
{
try {
// This may result in exception if the specified
// name is unknown.
TokenPtr opt(new TokenOption(yystack_[3].value.as< std::string > (), option_universe,
TokenOption::HEXADECIMAL));
ctx.expression.push_back(opt);
} catch (const std::exception& ex) {
ctx.error(yystack_[3].location, ex.what());
}
}
#line 684 "parser.cc" // lalr1.cc:859
break;
case 10:
#line 166 "parser.yy" // lalr1.cc:859
{
TokenPtr sub(new TokenSubstring());
ctx.expression.push_back(sub);
}
#line 648 "parser.cc" // lalr1.cc:859
#line 693 "parser.cc" // lalr1.cc:859
break;
case 10:
#line 143 "parser.yy" // lalr1.cc:859
case 12:
#line 175 "parser.yy" // lalr1.cc:859
{
TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
ctx.expression.push_back(str);
}
#line 657 "parser.cc" // lalr1.cc:859
#line 702 "parser.cc" // lalr1.cc:859
break;
case 11:
#line 150 "parser.yy" // lalr1.cc:859
case 13:
#line 182 "parser.yy" // lalr1.cc:859
{
TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
ctx.expression.push_back(str);
}
#line 666 "parser.cc" // lalr1.cc:859
#line 711 "parser.cc" // lalr1.cc:859
break;
case 12:
#line 155 "parser.yy" // lalr1.cc:859
case 14:
#line 187 "parser.yy" // lalr1.cc:859
{
TokenPtr str(new TokenString("all"));
ctx.expression.push_back(str);
}
#line 675 "parser.cc" // lalr1.cc:859
#line 720 "parser.cc" // lalr1.cc:859
break;
#line 679 "parser.cc" // lalr1.cc:859
#line 724 "parser.cc" // lalr1.cc:859
default:
break;
}
@@ -930,74 +975,79 @@ namespace isc { namespace eval {
}
const signed char EvalParser::yypact_ninf_ = -10;
const signed char EvalParser::yypact_ninf_ = -14;
const signed char EvalParser::yytable_ninf_ = -1;
const signed char
EvalParser::yypact_[] =
{
-4, -9, -3, -10, -10, -10, 9, -10, 12, 1,
-4, -10, -4, -2, 6, -10, 10, 2, 0, -10,
11, -10, -10, -6, -10, -10, 8, -10
-4, -9, -5, -14, -14, -14, 8, -14, 9, -13,
-4, -14, -4, 0, 6, 11, -14, 13, 14, 15,
10, 12, -14, 16, -14, -14, -14, -14, -6, -14,
-14, 17, -14
};
const unsigned char
EvalParser::yydefact_[] =
{
0, 0, 0, 4, 5, 9, 0, 2, 0, 0,
0, 1, 0, 0, 0, 3, 0, 0, 0, 10,
0, 6, 7, 0, 12, 11, 0, 8
0, 0, 0, 4, 5, 11, 0, 2, 0, 0,
0, 1, 0, 0, 0, 0, 3, 0, 0, 0,
0, 0, 12, 0, 6, 7, 8, 9, 0, 14,
13, 0, 10
};
const signed char
EvalParser::yypgoto_[] =
{
-10, -10, -10, -7, -10, -10
-14, -14, -14, -3, -14, -14
};
const signed char
EvalParser::yydefgoto_[] =
{
-1, 6, 7, 8, 20, 26
-1, 6, 7, 8, 23, 31
};
const unsigned char
EvalParser::yytable_[] =
{
1, 2, 24, 14, 9, 15, 21, 22, 10, 11,
25, 3, 16, 4, 5, 12, 17, 13, 19, 18,
27, 23
1, 2, 29, 13, 9, 14, 10, 15, 11, 16,
30, 3, 12, 4, 17, 5, 24, 25, 26, 27,
18, 19, 20, 21, 0, 0, 28, 0, 0, 32,
0, 22
};
const unsigned char
const signed char
EvalParser::yycheck_[] =
{
4, 5, 8, 10, 13, 12, 6, 7, 11, 0,
16, 15, 14, 17, 18, 3, 10, 16, 16, 9,
12, 10
4, 5, 8, 16, 13, 18, 11, 10, 0, 12,
16, 15, 3, 17, 14, 19, 6, 7, 6, 7,
14, 10, 9, 9, -1, -1, 10, -1, -1, 12,
-1, 16
};
const unsigned char
EvalParser::yystos_[] =
{
0, 4, 5, 15, 17, 18, 20, 21, 22, 13,
11, 0, 3, 16, 22, 22, 14, 10, 9, 16,
23, 6, 7, 10, 8, 16, 24, 12
0, 4, 5, 15, 17, 19, 21, 22, 23, 13,
11, 0, 3, 16, 18, 23, 23, 14, 14, 10,
9, 9, 16, 24, 6, 7, 6, 7, 10, 8,
16, 25, 12
};
const unsigned char
EvalParser::yyr1_[] =
{
0, 19, 20, 21, 22, 22, 22, 22, 22, 22,
23, 24, 24
0, 20, 21, 22, 23, 23, 23, 23, 23, 23,
23, 23, 24, 25, 25
};
const unsigned char
EvalParser::yyr2_[] =
{
0, 2, 1, 3, 1, 1, 6, 6, 8, 1,
1, 1, 1
0, 2, 1, 3, 1, 1, 6, 6, 6, 6,
8, 1, 1, 1, 1
};
@@ -1010,16 +1060,16 @@ namespace isc { namespace eval {
"\"end of file\"", "error", "$undefined", "\"==\"", "\"option\"",
"\"substring\"", "\"text\"", "\"hex\"", "\"all\"", "\".\"", "\",\"",
"\"(\"", "\")\"", "\"[\"", "\"]\"", "\"constant string\"", "\"integer\"",
"\"constant hexstring\"", "TOKEN", "$accept", "expression", "bool_expr",
"string_expr", "start_expr", "length_expr", YY_NULLPTR
"\"constant hexstring\"", "\"option name\"", "TOKEN", "$accept",
"expression", "bool_expr", "string_expr", "start_expr", "length_expr", YY_NULLPTR
};
#if YYDEBUG
const unsigned char
EvalParser::yyrline_[] =
{
0, 101, 101, 104, 111, 116, 121, 127, 133, 138,
142, 149, 154
0, 107, 107, 110, 117, 122, 127, 133, 139, 152,
165, 170, 174, 181, 186
};
// Print the state stack on the debug stream.
@@ -1054,8 +1104,8 @@ namespace isc { namespace eval {
#line 21 "parser.yy" // lalr1.cc:1167
} } // isc::eval
#line 1058 "parser.cc" // lalr1.cc:1167
#line 161 "parser.yy" // lalr1.cc:1168
#line 1108 "parser.cc" // lalr1.cc:1167
#line 193 "parser.yy" // lalr1.cc:1168
void
isc::eval::EvalParser::error(const location_type& loc,

View File

@@ -45,12 +45,13 @@
#include <string>
#include <eval/token.h>
#include <eval/eval_context_decl.h>
#include <dhcp/option.h>
#include <boost/lexical_cast.hpp>
using namespace isc::dhcp;
using namespace isc::eval;
#line 54 "parser.h" // lalr1.cc:392
#line 55 "parser.h" // lalr1.cc:392
# include <cassert>
# include <cstdlib> // std::abort
@@ -127,7 +128,7 @@ using namespace isc::eval;
#line 21 "parser.yy" // lalr1.cc:392
namespace isc { namespace eval {
#line 131 "parser.h" // lalr1.cc:392
#line 132 "parser.h" // lalr1.cc:392
@@ -297,6 +298,7 @@ namespace isc { namespace eval {
// "constant string"
// "integer"
// "constant hexstring"
// "option name"
// TOKEN
char dummy1[sizeof(std::string)];
};
@@ -337,7 +339,8 @@ namespace isc { namespace eval {
TOKEN_STRING = 270,
TOKEN_INTEGER = 271,
TOKEN_HEXSTRING = 272,
TOKEN_TOKEN = 273
TOKEN_OPTION_NAME = 273,
TOKEN_TOKEN = 274
};
};
@@ -508,13 +511,17 @@ namespace isc { namespace eval {
symbol_type
make_HEXSTRING (const std::string& v, const location_type& l);
static inline
symbol_type
make_OPTION_NAME (const std::string& v, const location_type& l);
static inline
symbol_type
make_TOKEN (const std::string& v, const location_type& l);
/// Build a parser object.
EvalParser (EvalContext& ctx_yyarg);
EvalParser (EvalContext& ctx_yyarg, const Option::Universe& option_universe_yyarg);
virtual ~EvalParser ();
/// Parse.
@@ -597,7 +604,7 @@ namespace isc { namespace eval {
// number is the opposite. If YYTABLE_NINF, syntax error.
static const unsigned char yytable_[];
static const unsigned char yycheck_[];
static const signed char yycheck_[];
// YYSTOS[STATE-NUM] -- The (internal number of the) accessing
// symbol of state STATE-NUM.
@@ -717,17 +724,18 @@ namespace isc { namespace eval {
enum
{
yyeof_ = 0,
yylast_ = 21, ///< Last index in yytable_.
yylast_ = 31, ///< Last index in yytable_.
yynnts_ = 6, ///< Number of nonterminal symbols.
yyfinal_ = 11, ///< Termination state number.
yyterror_ = 1,
yyerrcode_ = 256,
yyntokens_ = 19 ///< Number of tokens.
yyntokens_ = 20 ///< Number of tokens.
};
// User arguments.
EvalContext& ctx;
const Option::Universe& option_universe;
};
// Symbol number corresponding to token number t.
@@ -766,9 +774,9 @@ namespace isc { namespace eval {
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18
15, 16, 17, 18, 19
};
const unsigned int user_token_number_max_ = 273;
const unsigned int user_token_number_max_ = 274;
const token_number_type undef_token_ = 2;
if (static_cast<int>(t) <= yyeof_)
@@ -804,7 +812,8 @@ namespace isc { namespace eval {
case 15: // "constant string"
case 16: // "integer"
case 17: // "constant hexstring"
case 18: // TOKEN
case 18: // "option name"
case 19: // TOKEN
value.copy< std::string > (other.value);
break;
@@ -828,7 +837,8 @@ namespace isc { namespace eval {
case 15: // "constant string"
case 16: // "integer"
case 17: // "constant hexstring"
case 18: // TOKEN
case 18: // "option name"
case 19: // TOKEN
value.copy< std::string > (v);
break;
@@ -883,7 +893,8 @@ namespace isc { namespace eval {
case 15: // "constant string"
case 16: // "integer"
case 17: // "constant hexstring"
case 18: // TOKEN
case 18: // "option name"
case 19: // TOKEN
value.template destroy< std::string > ();
break;
@@ -913,7 +924,8 @@ namespace isc { namespace eval {
case 15: // "constant string"
case 16: // "integer"
case 17: // "constant hexstring"
case 18: // TOKEN
case 18: // "option name"
case 19: // TOKEN
value.move< std::string > (s.value);
break;
@@ -973,7 +985,7 @@ namespace isc { namespace eval {
yytoken_number_[] =
{
0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
265, 266, 267, 268, 269, 270, 271, 272, 273
265, 266, 267, 268, 269, 270, 271, 272, 273, 274
};
return static_cast<token_type> (yytoken_number_[type]);
}
@@ -1074,6 +1086,12 @@ namespace isc { namespace eval {
return symbol_type (token::TOKEN_HEXSTRING, v, l);
}
EvalParser::symbol_type
EvalParser::make_OPTION_NAME (const std::string& v, const location_type& l)
{
return symbol_type (token::TOKEN_OPTION_NAME, v, l);
}
EvalParser::symbol_type
EvalParser::make_TOKEN (const std::string& v, const location_type& l)
{
@@ -1083,7 +1101,7 @@ namespace isc { namespace eval {
#line 21 "parser.yy" // lalr1.cc:392
} } // isc::eval
#line 1087 "parser.h" // lalr1.cc:392
#line 1105 "parser.h" // lalr1.cc:392

View File

@@ -25,6 +25,7 @@
#include <string>
#include <eval/token.h>
#include <eval/eval_context_decl.h>
#include <dhcp/option.h>
#include <boost/lexical_cast.hpp>
using namespace isc::dhcp;
@@ -39,6 +40,10 @@ using namespace isc::eval;
{
# include "eval_context.h"
}
// Option universe: DHCPv4 or DHCPv6. This is required to use correct option
// definition set to map option names to codes.
%parse-param { const Option::Universe& option_universe }
%define api.token.prefix {TOKEN_}
%token
END 0 "end of file"
@@ -59,6 +64,7 @@ using namespace isc::eval;
%token <std::string> STRING "constant string"
%token <std::string> INTEGER "integer"
%token <std::string> HEXSTRING "constant hexstring"
%token <std::string> OPTION_NAME "option name"
%token <std::string> TOKEN
%printer { yyoutput << $$; } <*>;
@@ -130,6 +136,32 @@ string_expr : STRING
TokenPtr opt(new TokenOption(numeric_code, TokenOption::HEXADECIMAL));
ctx.expression.push_back(opt);
}
| OPTION "[" OPTION_NAME "]" DOT TEXT
{
try {
// This may result in exception if the specified
// name is unknown.
TokenPtr opt(new TokenOption($3, option_universe,
TokenOption::TEXTUAL));
ctx.expression.push_back(opt);
} catch (const std::exception& ex) {
ctx.error(@3, ex.what());
}
}
| OPTION "[" OPTION_NAME "]" DOT HEX
{
try {
// This may result in exception if the specified
// name is unknown.
TokenPtr opt(new TokenOption($3, option_universe,
TokenOption::HEXADECIMAL));
ctx.expression.push_back(opt);
} catch (const std::exception& ex) {
ctx.error(@3, ex.what());
}
}
| SUBSTRING "(" string_expr "," start_expr "," length_expr ")"
{
TokenPtr sub(new TokenSubstring());

View File

@@ -16,6 +16,7 @@
#include <eval/token.h>
#include <eval/eval_context.h>
#include <eval/token.h>
#include <dhcp/option.h>
#include <dhcp/pkt4.h>
#include <boost/shared_ptr.hpp>
@@ -94,7 +95,7 @@ public:
/// @brief checks if the given expression raises the expected message
/// when it is parsed.
void checkError(const string& expr, const string& msg) {
EvalContext eval;
EvalContext eval(Option::V4);
parsed_ = false;
try {
parsed_ = eval.parseString(expr);
@@ -115,15 +116,15 @@ public:
// Test the parsing of a basic expression
TEST_F(EvalContextTest, basic) {
EvalContext tmp;
EvalContext eval(Option::V4);
EXPECT_NO_THROW(parsed_ = tmp.parseString("option[123].text == 'MSFT'"));
EXPECT_NO_THROW(parsed_ = eval.parseString("option[123].text == 'MSFT'"));
EXPECT_TRUE(parsed_);
}
// Test the parsing of a string terminal
TEST_F(EvalContextTest, string) {
EvalContext eval;
EvalContext eval(Option::V4);
EXPECT_NO_THROW(parsed_ = eval.parseString("'foo' == 'bar'"));
EXPECT_TRUE(parsed_);
@@ -140,7 +141,7 @@ TEST_F(EvalContextTest, string) {
// Test the parsing of a basic expression using integers
TEST_F(EvalContextTest, integer) {
EvalContext eval;
EvalContext eval(Option::V4);
EXPECT_NO_THROW(parsed_ =
eval.parseString("substring(option[123].text, 0, 2) == '42'"));
@@ -149,7 +150,7 @@ TEST_F(EvalContextTest, integer) {
// Test the parsing of a hexstring terminal
TEST_F(EvalContextTest, hexstring) {
EvalContext eval;
EvalContext eval(Option::V4);
EXPECT_NO_THROW(parsed_ = eval.parseString("0x666f6f == 'foo'"));
EXPECT_TRUE(parsed_);
@@ -164,7 +165,7 @@ TEST_F(EvalContextTest, hexstring) {
// Test the parsing of a hexstring terminal with an odd number of
// hexadecimal digits
TEST_F(EvalContextTest, oddHexstring) {
EvalContext eval;
EvalContext eval(Option::V4);
EXPECT_NO_THROW(parsed_ = eval.parseString("0X7 == 'foo'"));
EXPECT_TRUE(parsed_);
@@ -178,7 +179,7 @@ TEST_F(EvalContextTest, oddHexstring) {
// Test the parsing of an equal expression
TEST_F(EvalContextTest, equal) {
EvalContext eval;
EvalContext eval(Option::V4);
EXPECT_NO_THROW(parsed_ = eval.parseString("'foo' == 'bar'"));
EXPECT_TRUE(parsed_);
@@ -196,7 +197,7 @@ TEST_F(EvalContextTest, equal) {
// Test the parsing of an option terminal
TEST_F(EvalContextTest, option) {
EvalContext eval;
EvalContext eval(Option::V4);
EXPECT_NO_THROW(parsed_ = eval.parseString("option[123].text == 'foo'"));
EXPECT_TRUE(parsed_);
@@ -204,9 +205,31 @@ TEST_F(EvalContextTest, option) {
checkTokenOption(eval.expression.at(0), 123);
}
// Test parsing of an option identified by name.
TEST_F(EvalContextTest, optionWithName) {
EvalContext eval(Option::V4);
// Option 'host-name' is a standard DHCPv4 option defined in the libdhcp++.
EXPECT_NO_THROW(parsed_ = eval.parseString("option[host-name].text == 'foo'"));
EXPECT_TRUE(parsed_);
ASSERT_EQ(3, eval.expression.size());
checkTokenOption(eval.expression.at(0), 12);
}
// Test checking that whitespace can surround option name.
TEST_F(EvalContextTest, optionWithNameAndWhitespace) {
EvalContext eval(Option::V4);
// Option 'host-name' is a standard DHCPv4 option defined in the libdhcp++.
EXPECT_NO_THROW(parsed_ = eval.parseString("option[ host-name ].text == 'foo'"));
EXPECT_TRUE(parsed_);
ASSERT_EQ(3, eval.expression.size());
checkTokenOption(eval.expression.at(0), 12);
}
// Test parsing of an option represented as hexadecimal string.
TEST_F(EvalContextTest, optionHex) {
EvalContext eval;
EvalContext eval(Option::V4);
EXPECT_NO_THROW(parsed_ = eval.parseString("option[123].hex == 0x666F6F"));
EXPECT_TRUE(parsed_);
@@ -216,7 +239,7 @@ TEST_F(EvalContextTest, optionHex) {
// Test the parsing of a substring expression
TEST_F(EvalContextTest, substring) {
EvalContext eval;
EvalContext eval(Option::V4);
EXPECT_NO_THROW(parsed_ =
eval.parseString("substring('foobar',2,all) == 'obar'"));
@@ -288,14 +311,12 @@ TEST_F(EvalContextTest, parseErrors) {
checkError("option(10) == 'ab'",
"<string>:1.7: syntax error, "
"unexpected (, expecting [");
checkError("option['ab'].text == 'foo'",
"<string>:1.8-11: syntax error, "
"unexpected constant string, "
"expecting integer");
checkError("option[ab].text == 'foo'",
"<string>:1.8-9: option 'ab' is not defined");
checkError("option[0xa].text == 'ab'",
"<string>:1.8-10: syntax error, "
"unexpected constant hexstring, "
"expecting integer");
"expecting integer or option name");
checkError("substring('foobar') == 'f'",
"<string>:1.19: syntax error, "
"unexpected ), expecting \",\"");

View File

@@ -217,7 +217,7 @@ TEST_F(EvaluateTest, optionHex) {
ASSERT_NO_THROW(toption.reset(new TokenOption(100, TokenOption::HEXADECIMAL)));
e_.push_back(toption);
ASSERT_NO_THROW(tstring.reset(new TokenString("0x68756E6472656434")));
ASSERT_NO_THROW(tstring.reset(new TokenString("hundred4")));
e_.push_back(tstring);
ASSERT_NO_THROW(tequal.reset(new TokenEqual()));
e_.push_back(tequal);

View File

@@ -14,6 +14,9 @@
#include <config.h>
#include <eval/token.h>
#include <dhcp/libdhcp++.h>
#include <dhcp/option.h>
#include <dhcp/option_definition.h>
#include <dhcp/pkt4.h>
#include <dhcp/pkt6.h>
#include <dhcp/dhcp4.h>
@@ -52,6 +55,13 @@ public:
pkt6_->addOption(option_str6_);
}
/// @brief Destructor.
///
/// Removes any option definitions registered in runtime.
virtual ~TokenTest() {
LibDHCP::clearRuntimeOptionDefs();
}
TokenPtr t_; ///< Just a convenience pointer
ValueStack values_; ///< evaluated values will be stored here
@@ -62,6 +72,23 @@ public:
OptionPtr option_str4_; ///< A string option for DHCPv4
OptionPtr option_str6_; ///< A string option for DHCPv6
/// @brief Create definitions of DHCPv4 options used by unit tests.
void createOptionDefinitions4() {
OptionDefSpaceContainer defs;
OptionDefinitionPtr opt_def4(new OptionDefinition("name-hundred4",
100, "string"));
defs.addItem(opt_def4, "dhcp4");
LibDHCP::setRuntimeOptionDefs(defs);
}
/// @brief Create definitions of DHCPv6 options used by unit tests.
void createOptionDefinitions6() {
OptionDefSpaceContainer defs;
OptionDefinitionPtr opt_def6(new OptionDefinition("name-hundred6",
100, "string"));
defs.addItem(opt_def6, "dhcp6");
LibDHCP::setRuntimeOptionDefs(defs);
}
/// @brief Verify that the substring eval works properly
///
@@ -274,6 +301,28 @@ TEST_F(TokenTest, optionString4) {
EXPECT_EQ("hundred4", values_.top());
}
// This test checks if a token representing an option identified by name is
// able to extract this option from an IPv4 packet and properly store the
// option's value.
TEST_F(TokenTest, optionWithNameString4) {
// Create definition of option 100 to provide a mapping between option
// code and option name.
ASSERT_NO_THROW(createOptionDefinitions4());
// Create token for option referenced by name. The constructor should
// map the option name to its code.
TokenPtr token;
ASSERT_NO_THROW(token.reset(new TokenOption("name-hundred4", Option::V4,
TokenOption::TEXTUAL)));
// Evaluate option in the packet.
ASSERT_NO_THROW(token->evaluate(*pkt4_, values_));
ASSERT_EQ(1, values_.size());
// Then the content of the option.
EXPECT_EQ("hundred4", values_.top());
}
// This test checks if a token representing option value is able to extract
// the option from an IPv4 packet and properly store its value in a
// hexadecimal format.
@@ -300,7 +349,29 @@ TEST_F(TokenTest, optionHexString4) {
values_.pop();
// Then the content of the option 100.
EXPECT_EQ("0x68756E6472656434", values_.top());
EXPECT_EQ("hundred4", values_.top());
}
// This test checks if a token representing an option identified by name is
// able to extract this option from an IPv4 packet and properly store its
// value in the hexadecimal format.
TEST_F(TokenTest, optionWithNameHexString4) {
// Create definition of option 100 to provide a mapping between option
// code and option name.
ASSERT_NO_THROW(createOptionDefinitions4());
// Create token for option referenced by name. The constructor should
// map the option name to its code.
TokenPtr token;
ASSERT_NO_THROW(token.reset(new TokenOption("name-hundred4", Option::V4,
TokenOption::HEXADECIMAL)));
// Evaluate option in the packet.
ASSERT_NO_THROW(token->evaluate(*pkt4_, values_));
ASSERT_EQ(1, values_.size());
// Then the content of the option.
EXPECT_EQ("hundred4", values_.top());
}
// This test checks if a token representing an option value is able to extract
@@ -331,6 +402,29 @@ TEST_F(TokenTest, optionString6) {
EXPECT_EQ("hundred6", values_.top());
}
// This test checks if a token representing an option identified by name is
// able to extract this option from an IPv6 packet and properly store the
// option's value.
TEST_F(TokenTest, optionWithNameString6) {
// Create definition of option 100 to provide a mapping between option
// code and option name.
ASSERT_NO_THROW(createOptionDefinitions6());
// Create token for option referenced by name. The constructor should
// map the option name to its code.
TokenPtr token;
ASSERT_NO_THROW(token.reset(new TokenOption("name-hundred6", Option::V6,
TokenOption::TEXTUAL)));
// Evaluate option in the packet.
ASSERT_NO_THROW(token->evaluate(*pkt6_, values_));
ASSERT_EQ(1, values_.size());
// Then the content of the option.
EXPECT_EQ("hundred6", values_.top());
}
// This test checks if a token representing an option value is able to extract
// the option from an IPv6 packet and properly store its value in hexadecimal
// format.
@@ -357,7 +451,29 @@ TEST_F(TokenTest, optionHexString6) {
values_.pop();
// Then the content of the option 100.
EXPECT_EQ("0x68756E6472656436", values_.top());
EXPECT_EQ("hundred6", values_.top());
}
// This test checks if a token representing an option identified by name is
// able to extract this option from an IPv6 packet and properly store its
// value in the hexadecimal format.
TEST_F(TokenTest, optionWithNameHexString6) {
// Create definition of option 100 to provide a mapping between option
// code and option name.
ASSERT_NO_THROW(createOptionDefinitions6());
// Create token for option referenced by name. The constructor should
// map the option name to its code.
TokenPtr token;
ASSERT_NO_THROW(token.reset(new TokenOption("name-hundred6", Option::V6,
TokenOption::HEXADECIMAL)));
// Evaluate option in the packet.
ASSERT_NO_THROW(token->evaluate(*pkt6_, values_));
ASSERT_EQ(1, values_.size());
// Then the content of the option.
EXPECT_EQ("hundred6", values_.top());
}
// This test checks if a token representing an == operator is able to

View File

@@ -14,6 +14,8 @@
#include <eval/token.h>
#include <eval/eval_log.h>
#include <dhcp/option_definition.h>
#include <dhcp/libdhcp++.h>
#include <util/encode/hex.h>
#include <boost/lexical_cast.hpp>
#include <cstring>
@@ -62,16 +64,42 @@ TokenHexString::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
values.push(value_);
}
TokenOption::TokenOption(const std::string& option_name,
const Option::Universe& option_universe,
const RepresentationType& rep_type)
: option_code_(0), representation_type_(rep_type) {
OptionDefinitionPtr option_def = LibDHCP::getOptionDef(option_universe,
option_name);
if (!option_def) {
const std::string global_space =
(option_universe == Option::V4) ? "dhcp4" : "dhcp6";
option_def = LibDHCP::getRuntimeOptionDef(global_space, option_name);
}
if (!option_def) {
isc_throw(BadValue, "option '" << option_name << "' is not defined");
}
option_code_ = option_def->getCode();
}
void
TokenOption::evaluate(const Pkt& pkt, ValueStack& values) {
OptionPtr opt = pkt.getOption(option_code_);
std::string opt_str;
if (opt) {
values.push(representation_type_ == TEXTUAL ? opt->toString()
: opt->toHexString());
} else {
// Option not found, push empty string
values.push("");
if (representation_type_ == TEXTUAL) {
opt_str = opt->toString();
} else {
std::vector<uint8_t> binary = opt->toBinary();
opt_str.assign(binary.begin(), binary.end());
}
}
// Push value of the option or empty string if there was no such option
// in the packet.
values.push(opt_str);
}
void

View File

@@ -16,8 +16,10 @@
#define TOKEN_H
#include <exceptions/exceptions.h>
#include <dhcp/option.h>
#include <dhcp/pkt.h>
#include <stack>
#include <string>
namespace isc {
namespace dhcp {
@@ -161,16 +163,24 @@ public:
};
/// @brief Constructor that takes an option code as a parameter
/// @param option_code code of the option
///
/// Note: There is no constructor that takes option_name, as it would
/// introduce complex dependency of the libkea-eval on libdhcpsrv.
///
/// @param option_code code of the option to be represented.
/// @param option_code Code of the option to be represented.
/// @param rep_type Token representation type.
TokenOption(const uint16_t option_code, const RepresentationType& rep_type)
: option_code_(option_code), representation_type_(rep_type) {}
/// @brief Constructor that takes option name as a parameter.
///
/// This constructor will throw exception if there is no definition using
/// specified option name in libdhcp++.
///
/// @param option_name Name of the option to be represented.
/// @param option_universe Option universe: DHCPv4 or DHCPv6.
/// @param rep_type Token representation type.
TokenOption(const std::string& option_name,
const Option::Universe& option_universe,
const RepresentationType& rep_type);
/// @brief Evaluates the values of the option
///
/// This token represents a value of the option, so this method attempts

View File

@@ -21,6 +21,7 @@ libkea_util_la_SOURCES += pointer_util.h
libkea_util_la_SOURCES += process_spawn.h process_spawn.cc
libkea_util_la_SOURCES += range_utilities.h
libkea_util_la_SOURCES += signal_set.cc signal_set.h
libkea_util_la_SOURCES += staged_value.h
libkea_util_la_SOURCES += stopwatch.cc stopwatch.h
libkea_util_la_SOURCES += stopwatch_impl.cc stopwatch_impl.h
libkea_util_la_SOURCES += versioned_csv_file.h versioned_csv_file.cc

126
src/lib/util/staged_value.h Normal file
View File

@@ -0,0 +1,126 @@
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef STAGED_VALUE_H
#define STAGED_VALUE_H
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
namespace isc {
namespace util {
/// @brief This class implements set/commit mechanism for a single object.
///
/// In some cases it is desired to set value for an object while keeping
/// ability to revert to an original value under certain conditions.
/// This is often desired for objects holding some part of application's
/// configuration. Configuration is usually a multi-step process and
/// may fail on almost any stage. If this happens, the last good
/// configuration should be used. This implies that some of the state
/// of some of the objects needs to be reverted.
///
/// This class implements a mechanism for setting and committing a value.
/// Until the new value has been committed it is possible to revert to
/// an original value.
///
/// @tparam ValueType Type of the value represented by this class.
template<typename ValueType>
class StagedValue : public boost::noncopyable {
public:
/// @brief Constructor.
///
/// Initializes the default value.
StagedValue()
: staging_(new ValueType()), current_(new ValueType()),
modified_(false) {
}
/// @brief Retrieves current value.
///
/// If the value hasn't been modified since last commit, reset or
/// revert operation, a committed value is returned. If the value
/// has been modified, the modified value is returned.
const ValueType& getValue() const {
return (modified_ ? *staging_ : *current_);
}
/// @brief Sets new value.
///
/// @param new_value New value to be assigned.
void setValue(const ValueType& new_value) {
*staging_ = new_value;
modified_ = true;
}
/// @brief Commits a value.
void commit() {
// Only apply changes if any modifications made.
if (modified_) {
current_ = staging_;
}
revert();
}
/// @brief Resets value to defaults.
void reset() {
revert();
current_.reset(new ValueType());
}
/// @brief Reverts any modifications since last commit.
void revert() {
staging_.reset(new ValueType());
modified_ = false;
}
/// @brief Assignment operator.
///
/// @param value New value to be assigned.
/// @return Reference to this.
StagedValue& operator=(const ValueType& value) {
setValue(value);
return (*this);
}
/// @brief Conversion operator to value type.
///
/// @return Reference to value represented by this object.
operator const ValueType&() const {
return (getValue());
}
private:
/// @brief Pointer to staging value.
///
/// This value holds any modifications made.
boost::shared_ptr<ValueType> staging_;
/// @brief Pointer to committed value.
///
/// This value holds last committed changes.
boost::shared_ptr<ValueType> current_;
/// @brief Boolean flag which indicates if any modifications have been
/// applied since last commit.
bool modified_;
};
} // namespace isc::util
} // namespace isc
#endif // STAGED_VALUE_H

View File

@@ -45,6 +45,7 @@ run_unittests_SOURCES += process_spawn_unittest.cc
run_unittests_SOURCES += qid_gen_unittest.cc
run_unittests_SOURCES += random_number_generator_unittest.cc
run_unittests_SOURCES += socketsession_unittest.cc
run_unittests_SOURCES += staged_value_unittest.cc
run_unittests_SOURCES += strutil_unittest.cc
run_unittests_SOURCES += time_utilities_unittest.cc
run_unittests_SOURCES += range_utilities_unittest.cc

View File

@@ -0,0 +1,114 @@
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <config.h>
#include <util/staged_value.h>
#include <boost/shared_ptr.hpp>
#include <gtest/gtest.h>
namespace {
using namespace isc::util;
// This test verifies that the value can be assigned and committed.
TEST(StagedValueTest, assignAndCommit) {
// Initally the value should be set to a default
StagedValue<int> value;
ASSERT_EQ(0, value.getValue());
// Set the new value without committing it and make sure it
// can be retrieved.
value.setValue(4);
ASSERT_EQ(4, value.getValue());
// Commit the value and make sure it still can be retrieved.
value.commit();
ASSERT_EQ(4, value.getValue());
// Set new value and retrieve it.
value.setValue(10);
ASSERT_EQ(10, value.getValue());
// Do it again and commit it.
value.setValue(20);
ASSERT_EQ(20, value.getValue());
value.commit();
EXPECT_EQ(20, value.getValue());
}
// This test verifies that the value can be reverted if it hasn't been
// committed.
TEST(StagedValueTest, revert) {
// Set the value and commit.
StagedValue<int> value;
value.setValue(123);
value.commit();
// Set new value and do not commit.
value.setValue(500);
// The new value should be the one returned.
ASSERT_EQ(500, value.getValue());
// But, reverting gets us back to original value.
value.revert();
EXPECT_EQ(123, value.getValue());
// Reverting again doesn't have any effect.
value.revert();
EXPECT_EQ(123, value.getValue());
}
// This test verifies that the value can be restored to an original one.
TEST(StagedValueTest, reset) {
// Set the new value and commit.
StagedValue<int> value;
value.setValue(123);
value.commit();
// Override the value but do not commit.
value.setValue(500);
// Resetting should take us back to default value.
value.reset();
EXPECT_EQ(0, value.getValue());
value.revert();
EXPECT_EQ(0, value.getValue());
}
// This test verifies that second commit doesn't modify a value.
TEST(StagedValueTest, commit) {
// Set the value and commit.
StagedValue<int> value;
value.setValue(123);
value.commit();
// Second commit should have no effect.
value.commit();
EXPECT_EQ(123, value.getValue());
}
// This test checks that type conversion operator works correctly.
TEST(StagedValueTest, conversionOperator) {
StagedValue<int> value;
value.setValue(244);
EXPECT_EQ(244, value);
}
// This test checks that the assignment operator works correctly.
TEST(StagedValueTest, assignmentOperator) {
StagedValue<int> value;
value = 111;
EXPECT_EQ(111, value);
}
} // end of anonymous namespace