mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-29 13:07:50 +00:00
[4096] Added parsers for client class definitions
New Files: parsers/client_class_def_parser.cc parsers/client_class_def_parser.h tests/client_class_def_parser_unittest.cc src/lib/dhcpsrv/parsers/Makefile.am Added entries for new files Added EXTRA_DIST entries for several files that were missing. src/lib/dhcpsrv/parsers/dhcp_parsers.h Added typedef for OptionDataListParserPtr src/lib/dhcpsrv/tests/Makefile.am Added entries for new unitest file src/lib/dhcpsrv/tests/client_class_def_unittest.cc Fixed broken unit test TEST(ClientClassDef, cfgOptionBasics)
This commit is contained in:
parent
ceda1e2cd9
commit
dd80413a61
@ -26,13 +26,20 @@ AM_CXXFLAGS = $(KEA_CXXFLAGS)
|
||||
# Whenever new file is added to the parsers folder, it must be
|
||||
# added here.
|
||||
EXTRA_DIST =
|
||||
EXTRA_DIST += parsers/client_class_def_parser.cc
|
||||
EXTRA_DIST += parsers/client_class_def_parser.h
|
||||
EXTRA_DIST += parsers/dhcp_config_parser.h
|
||||
EXTRA_DIST += parsers/dbaccess_parser.cc
|
||||
EXTRA_DIST += parsers/dbaccess_parser.h
|
||||
EXTRA_DIST += parsers/dhcp_parsers.cc
|
||||
EXTRA_DIST += parsers/dhcp_parsers.h
|
||||
EXTRA_DIST += parsers/expiration_config_parser.cc
|
||||
EXTRA_DIST += parsers/expiration_config_parser.h
|
||||
EXTRA_DIST += parsers/host_reservation_parser.cc
|
||||
EXTRA_DIST += parsers/host_reservation_parser.h
|
||||
EXTRA_DIST += parsers/host_reservations_list_parser.h
|
||||
EXTRA_DIST += parsers/ifaces_config_parser.cc
|
||||
EXTRA_DIST += parsers/ifaces_config_parser.h
|
||||
|
||||
# Define rule to build logging source files from message file
|
||||
alloc_engine_messages.h alloc_engine_messages.cc dhcpsrv_messages.h \
|
||||
@ -133,6 +140,8 @@ libkea_dhcpsrv_la_SOURCES += utils.h
|
||||
libkea_dhcpsrv_la_SOURCES += writable_host_data_source.h
|
||||
|
||||
# Configuration parsers
|
||||
libkea_dhcpsrv_la_SOURCES += parsers/client_class_def_parser.cc
|
||||
libkea_dhcpsrv_la_SOURCES += parsers/client_class_def_parser.h
|
||||
libkea_dhcpsrv_la_SOURCES += parsers/dhcp_config_parser.h
|
||||
libkea_dhcpsrv_la_SOURCES += parsers/dbaccess_parser.cc
|
||||
libkea_dhcpsrv_la_SOURCES += parsers/dbaccess_parser.h
|
||||
@ -156,6 +165,7 @@ libkea_dhcpsrv_la_CPPFLAGS = $(AM_CPPFLAGS)
|
||||
libkea_dhcpsrv_la_LIBADD = $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la
|
||||
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/stats/libkea-stats.la
|
||||
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la
|
||||
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/eval/libkea-eval.la
|
||||
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
|
||||
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
|
||||
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la
|
||||
|
154
src/lib/dhcpsrv/parsers/client_class_def_parser.cc
Normal file
154
src/lib/dhcpsrv/parsers/client_class_def_parser.cc
Normal file
@ -0,0 +1,154 @@
|
||||
// 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 <cc/data.h>
|
||||
#include <dhcpsrv/cfgmgr.h>
|
||||
#include <dhcpsrv/client_class_def.h>
|
||||
#include <dhcpsrv/parsers/client_class_def_parser.h>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
using namespace isc::data;
|
||||
|
||||
/// @file client_class_def.cc
|
||||
///
|
||||
/// @brief Method implementations for client class definition parsing
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
// ********************** ExpressionParser ****************************
|
||||
|
||||
ExpressionParser::ExpressionParser(const std::string&,
|
||||
ExpressionPtr& expression, ParserContextPtr global_context)
|
||||
: local_expression_(ExpressionPtr()), expression_(expression),
|
||||
global_context_(global_context) {
|
||||
}
|
||||
|
||||
void
|
||||
ExpressionParser::build(ConstElementPtr expression_cfg) {
|
||||
// Here is where we would call bison parser with our string
|
||||
// For now we'll just create an expression with a string token
|
||||
// containing the expression text
|
||||
try {
|
||||
if (expression_cfg->getType() != Element::string) {
|
||||
isc_throw(TypeError, "expression value must be a string");
|
||||
}
|
||||
std::string expression_text = expression_cfg->str();
|
||||
TokenPtr dummy(new TokenString(expression_text));
|
||||
local_expression_.reset(new Expression());
|
||||
local_expression_->push_back(dummy);
|
||||
} catch (const std::exception& ex) {
|
||||
// Append position if there is a failure.
|
||||
isc_throw(DhcpConfigError, ex.what() << " ("
|
||||
<< expression_cfg->getPosition() << ")");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExpressionParser::commit() {
|
||||
expression_ = local_expression_;
|
||||
}
|
||||
|
||||
// ********************** ClientClassDefParser ****************************
|
||||
|
||||
ClientClassDefParser::ClientClassDefParser(const std::string&,
|
||||
ClientClassDictionaryPtr& class_dictionary, ParserContextPtr global_context)
|
||||
: string_values_(new StringStorage()),
|
||||
match_expr_(ExpressionPtr()),
|
||||
options_(new CfgOption()),
|
||||
class_dictionary_(class_dictionary),
|
||||
global_context_(global_context) {
|
||||
}
|
||||
|
||||
void
|
||||
ClientClassDefParser::build(ConstElementPtr class_def_cfg) {
|
||||
// Parse the elements that make up the option definition.
|
||||
BOOST_FOREACH(ConfigPair param, class_def_cfg->mapValue()) {
|
||||
std::string entry(param.first);
|
||||
ParserPtr parser;
|
||||
if (entry == "name") {
|
||||
StringParserPtr str_parser(new StringParser(entry, string_values_));
|
||||
parser = str_parser;
|
||||
} else if (entry == "test") {
|
||||
ExpressionParserPtr exp_parser(new ExpressionParser(entry,
|
||||
match_expr_,
|
||||
global_context_));
|
||||
parser = exp_parser;
|
||||
} else if (entry == "option-data") {
|
||||
OptionDataListParserPtr opts_parser;
|
||||
uint16_t family = (global_context_->universe_ == Option::V4 ?
|
||||
AF_INET : AF_INET6);
|
||||
|
||||
opts_parser.reset(new OptionDataListParser(entry, options_, family));
|
||||
parser = opts_parser;
|
||||
} else {
|
||||
isc_throw(DhcpConfigError, "invalid parameter '" << entry
|
||||
<< "' (" << param.second->getPosition() << ")");
|
||||
}
|
||||
|
||||
parser->build(param.second);
|
||||
parser->commit();
|
||||
}
|
||||
|
||||
std::string name;
|
||||
try {
|
||||
name = string_values_->getParam("name");
|
||||
} catch (const std::exception& ex) {
|
||||
isc_throw(DhcpConfigError, ex.what() << " ("
|
||||
<< class_def_cfg->getPosition() << ")");
|
||||
}
|
||||
|
||||
try {
|
||||
// an OptionCollectionPtr
|
||||
class_dictionary_->addClass(name, match_expr_, options_);
|
||||
} catch (const std::exception& ex) {
|
||||
isc_throw(DhcpConfigError, ex.what()
|
||||
<< " (" << class_def_cfg->getPosition() << ")");
|
||||
}
|
||||
}
|
||||
|
||||
// ****************** ClientClassDefListParser ************************
|
||||
|
||||
ClientClassDefListParser::ClientClassDefListParser(const std::string&,
|
||||
ParserContextPtr
|
||||
global_context)
|
||||
: local_dictionary_(new ClientClassDictionary()),
|
||||
global_context_(global_context) {
|
||||
}
|
||||
|
||||
void
|
||||
ClientClassDefListParser::build(ConstElementPtr client_class_def_list) {
|
||||
if (!client_class_def_list) {
|
||||
isc_throw(DhcpConfigError, "parser error: a pointer to a list of"
|
||||
<< " client class definitions is NULL ("
|
||||
<< client_class_def_list->getPosition() << ")");
|
||||
}
|
||||
|
||||
BOOST_FOREACH(ConstElementPtr client_class_def,
|
||||
client_class_def_list->listValue()) {
|
||||
boost::shared_ptr<ClientClassDefParser>
|
||||
parser(new ClientClassDefParser("client-class-def",
|
||||
local_dictionary_,
|
||||
global_context_));
|
||||
parser->build(client_class_def);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ClientClassDefListParser::commit() {
|
||||
// CfgMgr::instance().setClientClassConfig(local_dictionary_);
|
||||
}
|
||||
|
||||
} // end of namespace isc::dhcp
|
||||
} // end of namespace isc
|
193
src/lib/dhcpsrv/parsers/client_class_def_parser.h
Normal file
193
src/lib/dhcpsrv/parsers/client_class_def_parser.h
Normal file
@ -0,0 +1,193 @@
|
||||
// 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 CLIENT_CLASS_DEF_PARSER_H
|
||||
#define CLIENT_CLASS_DEF_PARSER_H
|
||||
|
||||
#include <dhcpsrv/client_class_def.h>
|
||||
#include <dhcpsrv/parsers/dhcp_parsers.h>
|
||||
|
||||
/// @file client_class_def.h
|
||||
///
|
||||
/// @brief Parsers for client class definitions
|
||||
///
|
||||
/// These parsers are used to parse lists of client class definitions
|
||||
/// into a ClientClassDictionary of ClientClassDef instances. Each
|
||||
/// ClientClassDef consists of (at least) a name, an expression, and
|
||||
/// option-data. The latter two are currently optional.
|
||||
///
|
||||
/// There parsers defined are:
|
||||
///
|
||||
/// ClientClassDefListParser - creates a ClientClassDictionary from a list
|
||||
/// of element maps, where each map contains the entries that specifiy a
|
||||
/// single class. The names of the classes in the are expected to be
|
||||
/// unique. Attempting to define a duplicate class will result in an
|
||||
/// DhcpConfigError throw. Invoking @c commit() method causes the dictionary
|
||||
/// to be stored by the CfgMgr.
|
||||
///
|
||||
/// ClientClassDefParser - creates a ClientClassDefinition from an element
|
||||
/// map. The elements are as follows:
|
||||
///
|
||||
/// -# "name" - a string containing the name of the class
|
||||
///
|
||||
/// -# "test" - a string containing the logical expression used to determine
|
||||
/// membership in the class. @todo This is passed into the Bison parser.
|
||||
///
|
||||
/// -# "option-data" - a list which defines the options that should be
|
||||
/// assigned to memebers of the class. This element is optional and parsed
|
||||
/// using the @ref dhcp::OptionDataListParser.
|
||||
///
|
||||
/// ExpressionParser - creates an eval::Expression from a string element,
|
||||
/// using the Bison Parser @todo.
|
||||
///
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
/// @brief Parser for a logical expression
|
||||
///
|
||||
/// This parser creates an instance of an Expression from a string. The
|
||||
/// string is passed to the Bison Parser and the resultant Expression is
|
||||
/// stored into the ExpressionPtr reference passed into the constructor.
|
||||
class ExpressionParser : public DhcpConfigParser {
|
||||
public:
|
||||
/// @brief Constructor.
|
||||
///
|
||||
/// @param dummy first argument is ignored, all Parser constructors
|
||||
/// accept string as first argument.
|
||||
/// @param expression variable in which to store the new expression
|
||||
/// @param global_context is a pointer to the global context which
|
||||
/// stores global scope parameters, options, option defintions.
|
||||
ExpressionParser(const std::string& dummy, ExpressionPtr& expression,
|
||||
ParserContextPtr global_context);
|
||||
|
||||
/// @brief Parses an expression configuration element into an Expression
|
||||
///
|
||||
/// @param expression_cfg the configuration entry to be parsed.
|
||||
///
|
||||
/// @throw DhcpConfigError if parsing was unsuccessful.
|
||||
void build(isc::data::ConstElementPtr expression_cfg);
|
||||
|
||||
/// @brief Stores the parsed expression to the supplied storage.
|
||||
void commit();
|
||||
|
||||
private:
|
||||
/// @brief Local storage for the parsed expression
|
||||
ExpressionPtr local_expression_;
|
||||
|
||||
/// @brief Storage into which the parsed expression should be committed
|
||||
ExpressionPtr& expression_;
|
||||
|
||||
/// @brief Parsing context which contains global values, options and option
|
||||
/// definitions.
|
||||
ParserContextPtr global_context_;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<ExpressionParser> ExpressionParserPtr;
|
||||
|
||||
/// @brief Parser for a single client class definition.
|
||||
///
|
||||
/// This parser creates an instance of a client class definition.
|
||||
class ClientClassDefParser : public DhcpConfigParser {
|
||||
public:
|
||||
/// @brief Constructor.
|
||||
///
|
||||
/// @param dummy first argument is ignored, all Parser constructors
|
||||
/// accept string as first argument.
|
||||
/// @param class_dictionary dictionary into which the class should be added
|
||||
/// @param global_context is a pointer to the global context which
|
||||
/// stores global scope parameters, options, option defintions.
|
||||
ClientClassDefParser(const std::string& dummy,
|
||||
ClientClassDictionaryPtr& class_dictionary,
|
||||
ParserContextPtr global_context);
|
||||
|
||||
/// @brief Parses an entry that describes single client class definition.
|
||||
///
|
||||
/// Attempts to add the new class direclty into the given dictionary.
|
||||
/// This done here to detect duplicate classes prior to commit().
|
||||
/// @param client_class_def a configuration entry to be parsed.
|
||||
///
|
||||
/// @throw DhcpConfigError if parsing was unsuccessful.
|
||||
void build(isc::data::ConstElementPtr client_class_def);
|
||||
|
||||
/// @brief Does nothing.
|
||||
void commit() {};
|
||||
|
||||
private:
|
||||
|
||||
/// @brief Storage for class string values.
|
||||
StringStoragePtr string_values_;
|
||||
|
||||
/// @brief Storage for the class match expression
|
||||
ExpressionPtr match_expr_;
|
||||
|
||||
/// @brief Storage for the class options
|
||||
CfgOptionPtr options_;
|
||||
|
||||
/// @brief Dictionary to which the new class should be added
|
||||
ClientClassDictionaryPtr class_dictionary_;
|
||||
|
||||
/// @brief Parsing context which contains global values, options and option
|
||||
/// definitions.
|
||||
ParserContextPtr global_context_;
|
||||
};
|
||||
|
||||
/// @brief Defines a pointer to a ClientClassDefParser
|
||||
typedef boost::shared_ptr<ClientClassDefParser> ClientClassDefParserPtr;
|
||||
|
||||
/// @brief Parser for a list of client class definitions.
|
||||
///
|
||||
/// This parser iterates over all configuration entries that define
|
||||
/// client classes and creates ClientClassDef instances for each.
|
||||
/// If the parsing done in build() is successful, the collection of
|
||||
/// created definitions is given to the @todo CfgMgr.
|
||||
class ClientClassDefListParser : public DhcpConfigParser {
|
||||
public:
|
||||
/// @brief Constructor.
|
||||
///
|
||||
/// @param dummy first argument is ignored, all Parser constructors
|
||||
/// accept string as first argument.
|
||||
/// @param global_context is a pointer to the global context which
|
||||
/// stores global scope parameters, options, option defintions.
|
||||
ClientClassDefListParser(const std::string& dummy,
|
||||
ParserContextPtr global_context);
|
||||
|
||||
/// @brief Parse configuration entries.
|
||||
///
|
||||
/// This function parses configuration entries, creates instances
|
||||
/// of client class definitions and tries to adds them to the a
|
||||
/// local dictionary.
|
||||
///
|
||||
/// @param class_def_list pointer to an element that holds entries
|
||||
/// for client class definitions.
|
||||
/// @throw DhcpConfigError if configuration parsing fails.
|
||||
void build(isc::data::ConstElementPtr option_def_list);
|
||||
|
||||
/// @brief Commits class definitions to CfgMgr's global storage.
|
||||
void commit();
|
||||
|
||||
/// @brief Local class dictionary to store classes as they are being parsed
|
||||
ClientClassDictionaryPtr local_dictionary_;
|
||||
|
||||
/// Parsing context which contains global values, options and option
|
||||
/// definitions.
|
||||
ParserContextPtr global_context_;
|
||||
};
|
||||
|
||||
/// @brief Defines a pointer to a ClientClassDefListParser
|
||||
typedef boost::shared_ptr<ClientClassDefListParser> ClientClassDefListParserPtr;
|
||||
|
||||
} // end of namespace isc::dhcp
|
||||
} // end of namespace isc
|
||||
|
||||
#endif // CLIENT_CLASS_DEF_PARSER_H
|
@ -711,6 +711,8 @@ private:
|
||||
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<OptionDataListParser> OptionDataListParserPtr;
|
||||
|
||||
|
||||
/// @brief Parser for a single option definition.
|
||||
///
|
||||
|
@ -76,6 +76,7 @@ libdhcpsrv_unittests_SOURCES += cfg_subnets4_unittest.cc
|
||||
libdhcpsrv_unittests_SOURCES += cfg_subnets6_unittest.cc
|
||||
libdhcpsrv_unittests_SOURCES += cfgmgr_unittest.cc
|
||||
libdhcpsrv_unittests_SOURCES += client_class_def_unittest.cc
|
||||
libdhcpsrv_unittests_SOURCES += client_class_def_parser_unittest.cc
|
||||
libdhcpsrv_unittests_SOURCES += csv_lease_file4_unittest.cc
|
||||
libdhcpsrv_unittests_SOURCES += csv_lease_file6_unittest.cc
|
||||
libdhcpsrv_unittests_SOURCES += d2_client_unittest.cc
|
||||
@ -147,6 +148,7 @@ libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la
|
||||
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la
|
||||
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la
|
||||
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la
|
||||
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/eval/libkea-eval.la
|
||||
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
|
||||
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/tests/libdhcptest.la
|
||||
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
|
||||
|
459
src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc
Normal file
459
src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc
Normal file
@ -0,0 +1,459 @@
|
||||
// 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 <cc/data.h>
|
||||
#include <dhcpsrv/cfgmgr.h>
|
||||
#include <dhcpsrv/parsers/client_class_def_parser.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <sstream>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
/// @file client_class_def_parser_unittest.cc Unit tests for client class
|
||||
/// definition parsing.
|
||||
|
||||
using namespace isc::data;
|
||||
using namespace isc::dhcp;
|
||||
|
||||
namespace {
|
||||
|
||||
/// @brief Test fixture class for @c ClientClassDefParser.
|
||||
class ClientClassDefParserTest : public ::testing::Test {
|
||||
protected:
|
||||
|
||||
/// @brief Convenience method for parsing a configuration
|
||||
///
|
||||
/// Attempt to parse a given client class defintion.
|
||||
///
|
||||
/// @param config - JSON string containing the client class configuration
|
||||
/// to parse.
|
||||
/// @param universe - the universe in which the parsing context should
|
||||
/// occur.
|
||||
/// @return Returns a pointer to class instance created, or NULL if
|
||||
/// for some unforeseen reason it wasn't created in the local dictionary
|
||||
/// @throw indirectly, execptions convertring the JSON text to elements,
|
||||
/// or by the parsing itself are not caught
|
||||
ClientClassDefPtr parseClientClassDef(const std::string& config,
|
||||
Option::Universe universe) {
|
||||
// Create local dicitonary to which the parser add the class.
|
||||
ClientClassDictionaryPtr dictionary(new ClientClassDictionary());
|
||||
// Create the "global" context for the parser.
|
||||
ParserContextPtr context(new ParserContext(universe));
|
||||
|
||||
// Turn config into elements. This may emit exceptions.
|
||||
ElementPtr config_element = Element::fromJSON(config);
|
||||
|
||||
// Parse the configuration. This may emit exceptions.
|
||||
ClientClassDefParser parser("", dictionary, context);
|
||||
parser.build(config_element);
|
||||
|
||||
// If we didn't throw, then return the first and only class
|
||||
ClientClassDefMapPtr classes = dictionary->getClasses();
|
||||
ClientClassDefMap::iterator it = classes->begin();
|
||||
if (it != classes->end()) {
|
||||
return (*it).second;
|
||||
}
|
||||
|
||||
// Return NULL if for some reason the class doesn't exist.
|
||||
return (ClientClassDefPtr());
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief Test fixture class for @c ClientClassDefListParser.
|
||||
class ClientClassDefListParserTest : public ::testing::Test {
|
||||
protected:
|
||||
|
||||
/// @brief Convenience method for parsing a list of client class
|
||||
/// definitions.
|
||||
///
|
||||
/// Attempt to parse a given list of client class defintions into a
|
||||
/// ClientClassDictionary.
|
||||
///
|
||||
/// @param config - JSON string containing the list of definitions to parse.
|
||||
/// @param universe - the universe in which the parsing context should
|
||||
/// occur.
|
||||
/// @return Returns a pointer to class dictionary created
|
||||
/// @throw indirectly, execptions convertring the JSON text to elements,
|
||||
/// or by the parsing itself are not caught
|
||||
ClientClassDictionaryPtr parseClientClassDefList(const std::string& config,
|
||||
Option::Universe universe)
|
||||
{
|
||||
// Create the "global" context for the parser.
|
||||
ParserContextPtr context(new ParserContext(universe));
|
||||
|
||||
// Turn config into elements. This may emit exceptions.
|
||||
ElementPtr config_element = Element::fromJSON(config);
|
||||
|
||||
// Parse the configuration. This may emit exceptions.
|
||||
ClientClassDefListParser parser("", context);
|
||||
parser.build(config_element);
|
||||
|
||||
// Return the parser's local dicationary
|
||||
return (parser.local_dictionary_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Verifies basic operation of an ExpressionParser. Until we tie
|
||||
// this into the actual Bison parsing there's not much to test.
|
||||
TEST(ExpressionParserTest, simpleStringExpression) {
|
||||
ParserContextPtr context(new ParserContext(Option::V4));
|
||||
ExpressionParserPtr parser;
|
||||
ExpressionPtr parsed_expr;
|
||||
|
||||
// Turn config into elements. This may emit exceptions.
|
||||
std::string cfg_txt = "\"astring\"";
|
||||
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);
|
||||
|
||||
// Evaluate it. For now the result will be the
|
||||
// expression string as dummy ExpressionParser
|
||||
// just makes an expression of one TokenString
|
||||
// containing the expression string itself.
|
||||
ValueStack vstack;
|
||||
Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, 12345));
|
||||
(*parsed_expr)[0]->evaluate(*pkt4, vstack);
|
||||
EXPECT_EQ(vstack.top(), "\"astring\"");
|
||||
}
|
||||
|
||||
// Verifies that given an invalid expression, the Expression parser
|
||||
// will throw a DhdpConfigError. Note this 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. For now, since
|
||||
// our parser is a dummy parser which only checks that it's given
|
||||
// Element::string so send it an integer.
|
||||
TEST(ExpressionParserTest, invalidExpression) {
|
||||
ParserContextPtr context(new ParserContext(Option::V4));
|
||||
ExpressionParserPtr parser;
|
||||
ExpressionPtr parsed_expr;
|
||||
|
||||
// Turn config into elements.
|
||||
std::string cfg_txt = "777";
|
||||
ElementPtr config_element = Element::fromJSON(cfg_txt);
|
||||
|
||||
// Create the parser.
|
||||
ASSERT_NO_THROW(parser.reset(new ExpressionParser("", parsed_expr,
|
||||
context)));
|
||||
// Expressionn build() should fail.
|
||||
ASSERT_THROW(parser->build(config_element), DhcpConfigError);
|
||||
}
|
||||
|
||||
// Verifies you can create a class with only a name
|
||||
// Whether that's useful or not, remains to be seen.
|
||||
// For now the class allows it.
|
||||
TEST_F(ClientClassDefParserTest, nameOnlyValid) {
|
||||
std::string cfg_text =
|
||||
"{ \n"
|
||||
" \"name\": \"MICROSOFT\" \n"
|
||||
"} \n";
|
||||
|
||||
ClientClassDefPtr cclass;
|
||||
ASSERT_NO_THROW(cclass = parseClientClassDef(cfg_text, Option::V4));
|
||||
|
||||
// We should find our class.
|
||||
ASSERT_TRUE(cclass);
|
||||
EXPECT_EQ("MICROSOFT", cclass->getName());
|
||||
|
||||
// CfgOption should be a non-null pointer but there
|
||||
// should be no options. Currently there's no good
|
||||
// way to test that there no options.
|
||||
CfgOptionPtr cfg_option;
|
||||
cfg_option = cclass->getCfgOption();
|
||||
ASSERT_TRUE(cfg_option);
|
||||
OptionContainerPtr oc;
|
||||
ASSERT_TRUE(oc = cclass->getCfgOption()->getAll("dhcp4"));
|
||||
EXPECT_EQ(0, oc->size());
|
||||
|
||||
// Verify we have no expression.
|
||||
ASSERT_FALSE(cclass->getMatchExpr());
|
||||
}
|
||||
|
||||
// Verifies you can create a class with a name, expression,
|
||||
// but no options.
|
||||
TEST_F(ClientClassDefParserTest, nameAndExpressionClass) {
|
||||
|
||||
std::string cfg_text =
|
||||
"{ \n"
|
||||
" \"name\": \"MICROSOFT\", \n"
|
||||
" \"test\": \"vendor-class-identifier == 'MSFT'\" \n"
|
||||
"} \n";
|
||||
|
||||
ClientClassDefPtr cclass;
|
||||
ASSERT_NO_THROW(cclass = parseClientClassDef(cfg_text, Option::V4));
|
||||
|
||||
// We should find our class.
|
||||
ASSERT_TRUE(cclass);
|
||||
EXPECT_EQ("MICROSOFT", cclass->getName());
|
||||
|
||||
// CfgOption should be a non-null pointer but there
|
||||
// should be no options. Currently there's no good
|
||||
// way to test that there no options.
|
||||
CfgOptionPtr cfg_option;
|
||||
cfg_option = cclass->getCfgOption();
|
||||
ASSERT_TRUE(cfg_option);
|
||||
OptionContainerPtr oc;
|
||||
ASSERT_TRUE(oc = cclass->getCfgOption()->getAll("dhcp4"));
|
||||
EXPECT_EQ(0, oc->size());
|
||||
|
||||
// Verify we can retrieve the expression
|
||||
ExpressionPtr match_expr = cclass->getMatchExpr();
|
||||
ASSERT_TRUE(match_expr);
|
||||
|
||||
// Evaluate it. For now the result will be the
|
||||
// expression string as dummy ExpressionParser
|
||||
// just makes an expression of one TokenString
|
||||
// containing the expression string itself.
|
||||
ValueStack vstack;
|
||||
Pkt4Ptr pkt4;
|
||||
pkt4.reset(new Pkt4(DHCPDISCOVER, 12345));
|
||||
(*match_expr)[0]->evaluate(*pkt4, vstack);
|
||||
EXPECT_EQ(vstack.top(), "\"vendor-class-identifier == 'MSFT'\"");
|
||||
}
|
||||
|
||||
// Verifies you can create a class with a name and options,
|
||||
// but no expression.
|
||||
TEST_F(ClientClassDefParserTest, nameAndOptionsClass) {
|
||||
|
||||
std::string cfg_text =
|
||||
"{ \n"
|
||||
" \"name\": \"MICROSOFT\", \n"
|
||||
" \"option-data\": [ \n"
|
||||
" { \n"
|
||||
" \"name\": \"domain-name-servers\", \n"
|
||||
" \"code\": 6, \n"
|
||||
" \"space\": \"dhcp4\", \n"
|
||||
" \"csv-format\": true, \n"
|
||||
" \"data\": \"192.0.2.1, 192.0.2.2\" \n"
|
||||
" } \n"
|
||||
" ] \n"
|
||||
"} \n";
|
||||
|
||||
ClientClassDefPtr cclass;
|
||||
ASSERT_NO_THROW(cclass = parseClientClassDef(cfg_text, Option::V4));
|
||||
|
||||
// We should find our class.
|
||||
ASSERT_TRUE(cclass);
|
||||
EXPECT_EQ("MICROSOFT", cclass->getName());
|
||||
|
||||
// Our one option should exist.
|
||||
OptionDescriptor od = cclass->getCfgOption()->get("dhcp4", 6);
|
||||
ASSERT_TRUE(od.option_);
|
||||
EXPECT_EQ(6, od.option_->getType());
|
||||
|
||||
// Verify we have no expression
|
||||
ASSERT_FALSE(cclass->getMatchExpr());
|
||||
}
|
||||
|
||||
|
||||
// Verifies you can create a class with a name, expression,
|
||||
// and options.
|
||||
TEST_F(ClientClassDefParserTest, basicValidClass) {
|
||||
|
||||
std::string cfg_text =
|
||||
"{ \n"
|
||||
" \"name\": \"MICROSOFT\", \n"
|
||||
" \"test\": \"vendor-class-identifier == 'MSFT'\", \n"
|
||||
" \"option-data\": [ \n"
|
||||
" { \n"
|
||||
" \"name\": \"domain-name-servers\", \n"
|
||||
" \"code\": 6, \n"
|
||||
" \"space\": \"dhcp4\", \n"
|
||||
" \"csv-format\": true, \n"
|
||||
" \"data\": \"192.0.2.1, 192.0.2.2\" \n"
|
||||
" } \n"
|
||||
" ] \n"
|
||||
"} \n";
|
||||
|
||||
ClientClassDefPtr cclass;
|
||||
ASSERT_NO_THROW(cclass = parseClientClassDef(cfg_text, Option::V4));
|
||||
|
||||
// We should find our class.
|
||||
ASSERT_TRUE(cclass);
|
||||
EXPECT_EQ("MICROSOFT", cclass->getName());
|
||||
|
||||
// Our one option should exist.
|
||||
OptionDescriptor od = cclass->getCfgOption()->get("dhcp4", 6);
|
||||
ASSERT_TRUE(od.option_);
|
||||
EXPECT_EQ(6, od.option_->getType());
|
||||
|
||||
// Verify we can retrieve the expression
|
||||
ExpressionPtr match_expr = cclass->getMatchExpr();
|
||||
ASSERT_TRUE(match_expr);
|
||||
|
||||
// Evaluate it. For now the result will be the
|
||||
// expression string as dummy ExpressionParser
|
||||
// just makes an expression of one TokenString
|
||||
// containing the expression string itself.
|
||||
ValueStack vstack;
|
||||
Pkt4Ptr pkt4;
|
||||
pkt4.reset(new Pkt4(DHCPDISCOVER, 12345));
|
||||
(*match_expr)[0]->evaluate(*pkt4, vstack);
|
||||
EXPECT_EQ(vstack.top(), "\"vendor-class-identifier == 'MSFT'\"");
|
||||
}
|
||||
|
||||
// Verifies that a class with no name, fails to parse.
|
||||
TEST_F(ClientClassDefParserTest, noClassName) {
|
||||
|
||||
std::string cfg_text =
|
||||
"{ \n"
|
||||
" \"test\": \"vendor-class-identifier == 'MSFT'\", \n"
|
||||
" \"option-data\": [ \n"
|
||||
" { \n"
|
||||
" \"name\": \"domain-name-servers\", \n"
|
||||
" \"code\": 6, \n"
|
||||
" \"space\": \"dhcp4\", \n"
|
||||
" \"csv-format\": true, \n"
|
||||
" \"data\": \"192.0.2.1, 192.0.2.2\" \n"
|
||||
" } \n"
|
||||
" ] \n"
|
||||
"} \n";
|
||||
|
||||
ClientClassDefPtr cclass;
|
||||
ASSERT_THROW(cclass = parseClientClassDef(cfg_text, Option::V4),
|
||||
DhcpConfigError);
|
||||
}
|
||||
|
||||
// Verifies that a class with an unknown element, fails to parse.
|
||||
TEST_F(ClientClassDefParserTest, unknownElement) {
|
||||
std::string cfg_text =
|
||||
"{ \n"
|
||||
" \"name\": \"one\", \n"
|
||||
" \"bogus\": \"bad\" \n"
|
||||
"} \n";
|
||||
|
||||
ClientClassDefPtr cclass;
|
||||
ASSERT_THROW(cclass = parseClientClassDef(cfg_text, Option::V4),
|
||||
DhcpConfigError);
|
||||
}
|
||||
|
||||
// Verifies that a class with an invalid expression, fails to parse.
|
||||
TEST_F(ClientClassDefParserTest, invalidExpression) {
|
||||
std::string cfg_text =
|
||||
"{ \n"
|
||||
" \"name\": \"one\", \n"
|
||||
" \"test\": 777 \n"
|
||||
"} \n";
|
||||
|
||||
ClientClassDefPtr cclass;
|
||||
ASSERT_THROW(cclass = parseClientClassDef(cfg_text, Option::V4),
|
||||
DhcpConfigError);
|
||||
}
|
||||
|
||||
// Verifies that a class with invalid option-data, fails to parse.
|
||||
TEST_F(ClientClassDefParserTest, invalidOptionData) {
|
||||
std::string cfg_text =
|
||||
"{ \n"
|
||||
" \"name\": \"one\", \n"
|
||||
" \"option-data\": [ \n"
|
||||
" { \"bogus\": \"bad\" } \n"
|
||||
" ] \n"
|
||||
"} \n";
|
||||
|
||||
ClientClassDefPtr cclass;
|
||||
ASSERT_THROW(cclass = parseClientClassDef(cfg_text, Option::V4),
|
||||
DhcpConfigError);
|
||||
}
|
||||
|
||||
|
||||
// Verifies that a valid list of client classes will parse.
|
||||
TEST_F(ClientClassDefListParserTest, simpleValidList) {
|
||||
std::string cfg_text =
|
||||
"[ \n"
|
||||
" { \n"
|
||||
" \"name\": \"one\" \n"
|
||||
" }, \n"
|
||||
" { \n"
|
||||
" \"name\": \"two\" \n"
|
||||
" }, \n"
|
||||
" { \n"
|
||||
" \"name\": \"three\" \n"
|
||||
" } \n"
|
||||
"] \n";
|
||||
|
||||
// Parsing the list should succeed.
|
||||
ClientClassDictionaryPtr dictionary;
|
||||
ASSERT_NO_THROW(dictionary = parseClientClassDefList(cfg_text, Option::V4));
|
||||
ASSERT_TRUE(dictionary);
|
||||
|
||||
// We should have three classes in the dictionary.
|
||||
EXPECT_EQ(3, dictionary->getClasses()->size());
|
||||
|
||||
// Make sure we can find all three.
|
||||
ClientClassDefPtr cclass;
|
||||
ASSERT_NO_THROW(cclass = dictionary->findClass("one"));
|
||||
ASSERT_TRUE(cclass);
|
||||
EXPECT_EQ("one", cclass->getName());
|
||||
|
||||
ASSERT_NO_THROW(cclass = dictionary->findClass("two"));
|
||||
ASSERT_TRUE(cclass);
|
||||
EXPECT_EQ("two", cclass->getName());
|
||||
|
||||
ASSERT_NO_THROW(cclass = dictionary->findClass("three"));
|
||||
ASSERT_TRUE(cclass);
|
||||
EXPECT_EQ("three", cclass->getName());
|
||||
|
||||
// For good measure, make sure we can't find a non-existant class.
|
||||
ASSERT_NO_THROW(cclass = dictionary->findClass("bogus"));
|
||||
EXPECT_FALSE(cclass);
|
||||
}
|
||||
|
||||
// Verifies that class list containing a duplicate class entries, fails
|
||||
// to parse.
|
||||
TEST_F(ClientClassDefListParserTest, duplicateClass) {
|
||||
std::string cfg_text =
|
||||
"[ \n"
|
||||
" { \n"
|
||||
" \"name\": \"one\" \n"
|
||||
" }, \n"
|
||||
" { \n"
|
||||
" \"name\": \"two\" \n"
|
||||
" }, \n"
|
||||
" { \n"
|
||||
" \"name\": \"two\" \n"
|
||||
" } \n"
|
||||
"] \n";
|
||||
|
||||
ClientClassDictionaryPtr dictionary;
|
||||
ASSERT_THROW(dictionary = parseClientClassDefList(cfg_text, Option::V4),
|
||||
DhcpConfigError);
|
||||
}
|
||||
|
||||
// Verifies that a class list containing an invalid class entry, fails to
|
||||
// parse.
|
||||
TEST_F(ClientClassDefListParserTest, invalidClass) {
|
||||
std::string cfg_text =
|
||||
"[ \n"
|
||||
" { \n"
|
||||
" \"name\": \"one\", \n"
|
||||
" \"bogus\": \"bad\" \n"
|
||||
" } \n"
|
||||
"] \n";
|
||||
|
||||
ClientClassDictionaryPtr dictionary;
|
||||
ASSERT_THROW(dictionary = parseClientClassDefList(cfg_text, Option::V4),
|
||||
DhcpConfigError);
|
||||
}
|
||||
|
||||
} // end of anonymous namespace
|
@ -19,6 +19,9 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
/// @file client_class_def_unittest.cc Unit tests for client class storage
|
||||
/// classes.
|
||||
|
||||
using namespace std;
|
||||
using namespace isc::dhcp;
|
||||
using namespace isc::util;
|
||||
@ -35,7 +38,8 @@ TEST(ClientClassDef, construction) {
|
||||
CfgOptionPtr cfg_option;
|
||||
|
||||
// Classes cannot have blank names
|
||||
ASSERT_THROW(cclass.reset(new ClientClassDef("", expr, cfg_option)), BadValue);
|
||||
ASSERT_THROW(cclass.reset(new ClientClassDef("", expr, cfg_option)),
|
||||
BadValue);
|
||||
|
||||
// Verify we can create a class with a name, expression, and no cfg_option
|
||||
ASSERT_NO_THROW(cclass.reset(new ClientClassDef(name, expr)));
|
||||
@ -87,7 +91,7 @@ TEST(ClientClassDef, cfgOptionBasics) {
|
||||
// Now make sure we can find all the options
|
||||
OptionDescriptor opt_desc = class_options->get("dhcp4",17);
|
||||
ASSERT_TRUE(opt_desc.option_);
|
||||
EXPECT_EQ(100, opt_desc.option_->getType());
|
||||
EXPECT_EQ(17, opt_desc.option_->getType());
|
||||
|
||||
opt_desc = class_options->get("isc",101);
|
||||
ASSERT_TRUE(opt_desc.option_);
|
||||
|
Loading…
x
Reference in New Issue
Block a user