#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/client_class_def.h>
#include <dhcpsrv/parsers/client_class_def_parser.h>
+#include <eval/eval_context.h>
+
#include <boost/foreach.hpp>
using namespace isc::data;
ExpressionParser::ExpressionParser(const std::string&,
ExpressionPtr& expression, ParserContextPtr global_context)
- : local_expression_(ExpressionPtr()), expression_(expression),
+ : 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
+ if (expression_cfg->getType() != Element::string) {
+ isc_throw(DhcpConfigError, "expression ["
+ << expression_cfg->str() << "] must be a string, at ("
+ << expression_cfg->getPosition() << ")");
+ }
+
+ // Get the expression's text via getValue() as the text returned
+ // by str() enclosed in quotes.
+ std::string value;
+ expression_cfg->getValue(value);
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));
+ EvalContext eval_ctx;
+ eval_ctx.parseString(value);
local_expression_.reset(new Expression());
- local_expression_->push_back(dummy);
+ *local_expression_ = eval_ctx.expression;
} catch (const std::exception& ex) {
// Append position if there is a failure.
- isc_throw(DhcpConfigError, ex.what() << " ("
- << expression_cfg->getPosition() << ")");
+ isc_throw(DhcpConfigError,
+ "expression: [" << value
+ << "] error: " << ex.what() << " at ("
+ << expression_cfg->getPosition() << ")");
}
}
// ****************** ClientClassDefListParser ************************
ClientClassDefListParser::ClientClassDefListParser(const std::string&,
- ParserContextPtr
+ ParserContextPtr
global_context)
- : local_dictionary_(new ClientClassDictionary()),
+ : local_dictionary_(new ClientClassDictionary()),
global_context_(global_context) {
}
<< client_class_def_list->getPosition() << ")");
}
- BOOST_FOREACH(ConstElementPtr client_class_def,
+ BOOST_FOREACH(ConstElementPtr client_class_def,
client_class_def_list->listValue()) {
boost::shared_ptr<ClientClassDefParser>
- parser(new ClientClassDefParser("client-class-def",
+ parser(new ClientClassDefParser("client-class-def",
local_dictionary_,
global_context_));
parser->build(client_class_def);
#include <config.h>
#include <cc/data.h>
+#include <dhcp/option_string.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/parsers/client_class_def_parser.h>
+#include <eval/evaluate.h>
#include <gtest/gtest.h>
#include <sstream>
#include <stdint.h>
}
};
-
-// Verifies basic operation of an ExpressionParser. Until we tie
-// this into the actual Bison parsing there's not much to test.
-TEST(ExpressionParserTest, simpleStringExpression) {
+// 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;
// Turn config into elements. This may emit exceptions.
- std::string cfg_txt = "\"astring\"";
+ std::string cfg_txt = "\"option[100] == 'hundred4'\"";
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.
+ (parser->build(config_element));
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\"");
+ // 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 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] == '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.
+ (parser->build(config_element));
+ 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));
}
-// 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) {
+
+// Verifies that an the ExpressionParser only accepts StringElements.
+TEST(ExpressionParserTest, invalidExpressionElement) {
ParserContextPtr context(new ParserContext(Option::V4));
ExpressionParserPtr parser;
ExpressionPtr parsed_expr;
- // Turn config into elements.
+ // This will create an integer element should fail.
std::string cfg_txt = "777";
ElementPtr config_element = Element::fromJSON(cfg_txt);
ASSERT_THROW(parser->build(config_element), DhcpConfigError);
}
+// Verifies that given an invalid expression with a syntax error,
+// 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.
+TEST(ExpressionParserTest, expressionSyntaxError) {
+ ParserContextPtr context(new ParserContext(Option::V4));
+ ExpressionParserPtr parser;
+ ExpressionPtr parsed_expr;
+
+ // Turn config into elements.
+ std::string cfg_txt = "\"option 'bogus'\"";
+ 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.
std::string cfg_text =
"{ \n"
- " \"name\": \"MICROSOFT\", \n"
- " \"test\": \"vendor-class-identifier == 'MSFT'\" \n"
+ " \"name\": \"class_one\", \n"
+ " \"test\": \"option[100] == 'works right'\" \n"
"} \n";
ClientClassDefPtr cclass;
// We should find our class.
ASSERT_TRUE(cclass);
- EXPECT_EQ("MICROSOFT", cclass->getName());
+ EXPECT_EQ("class_one", cclass->getName());
// CfgOption should be a non-null pointer but there
// should be no options. Currently there's no good
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'\"");
+ // Build a packet that will fail evaluation.
+ Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, 123));
+ EXPECT_FALSE(evaluate(*match_expr, *pkt4));
+
+ // Now add the option so it will pass.
+ OptionPtr opt(new OptionString(Option::V4, 100, "works right"));
+ pkt4->addOption(opt);
+ EXPECT_TRUE(evaluate(*match_expr, *pkt4));
}
// Verifies you can create a class with a name and options,
std::string cfg_text =
"{ \n"
" \"name\": \"MICROSOFT\", \n"
- " \"test\": \"vendor-class-identifier == 'MSFT'\", \n"
+ " \"test\": \"option[100] == 'booya'\", \n"
" \"option-data\": [ \n"
" { \n"
" \"name\": \"domain-name-servers\", \n"
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'\"");
+ // Build a packet that will fail evaluation.
+ Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, 123));
+ EXPECT_FALSE(evaluate(*match_expr, *pkt4));
+
+ // Now add the option so it will pass.
+ OptionPtr opt(new OptionString(Option::V4, 100, "booya"));
+ pkt4->addOption(opt);
+ EXPECT_TRUE(evaluate(*match_expr, *pkt4));
}
// Verifies that a class with no name, fails to parse.