if (audit_entries.empty() || !updated_entries.empty()) {
ClientClassDictionary client_classes = getMgr().getPool()->getAllClientClasses4(backend_selector,
server_selector);
+ // Match expressions are not initialized for classes returned from the config backend.
+ // We have to ensure to initialize them before they can be used by the server.
+ client_classes.initMatchExpr(AF_INET);
external_cfg->setClientClassDictionary(boost::make_shared<ClientClassDictionary>(client_classes));
}
if (audit_entries.empty() || !updated_entries.empty()) {
ClientClassDictionary client_classes = getMgr().getPool()->getAllClientClasses6(backend_selector,
server_selector);
+ // Match expressions are not initialized for classes returned from the config backend.
+ // We have to ensure to initialize them before they can be used by the server.
+ client_classes.initMatchExpr(AF_INET6);
external_cfg->setClientClassDictionary(boost::make_shared<ClientClassDictionary>(client_classes));
}
#include <eval/dependency.h>
#include <dhcpsrv/client_class_def.h>
#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/parsers/client_class_def_parser.h>
#include <boost/foreach.hpp>
+#include <queue>
+
using namespace isc::data;
namespace isc {
return (true);
}
+void
+ClientClassDictionary::initMatchExpr(uint16_t family) {
+ std::queue<ExpressionPtr> expressions;
+ for (auto c : *list_) {
+ ExpressionPtr match_expr = boost::make_shared<Expression>();
+ if (!c->getTest().empty()) {
+ ExpressionParser parser;
+ parser.parse(match_expr, Element::create(c->getTest()), family);
+ }
+ expressions.push(match_expr);
+ }
+ // All expressions successfully initialied. Let's set them for the
+ // client classes in the dictionary.
+ for (auto c : *list_) {
+ c->setMatchExpr(expressions.front());
+ expressions.pop();
+ }
+}
+
ElementPtr
ClientClassDictionary::toElement() const {
ElementPtr result = Element::createList();
/// @return true if descriptors equal, false otherwise.
bool equals(const ClientClassDictionary& other) const;
+ /// @brief Iterates over the classes in the dictionary and ensures that
+ /// that match expressions are initialized.
+ ///
+ /// @param family Class universe, e.g. AF_INET or AF_INET6.
+ void initMatchExpr(uint16_t family);
+
/// @brief Equality operator.
///
/// @param other Other client class dictionary to compare to.
// Insert client classes into the database.
auto expression = boost::make_shared<Expression>();
ClientClassDefPtr client_class = boost::make_shared<ClientClassDef>("first-class", expression);
+ client_class->setTest("substring(option[1].hex, 0, 8) == 'my-value'");
client_class->setId(1);
client_class->setModificationTime(getTimestamp("dhcp4_client_class"));
mgr.getPool()->createUpdateClientClass4(BackendSelector::UNSPEC(), ServerSelector::ALL(),
if (hasConfigElement("dhcp4_client_class") &&
(getTimestamp("dhcp4_client_class") > lb_modification_time)) {
ASSERT_TRUE(found_class);
+ ASSERT_TRUE(found_class->getMatchExpr());
+ EXPECT_GT(found_class->getMatchExpr()->size(), 0);
EXPECT_EQ("first-class", found_class->getName());
} else {
// Insert client classes into the database.
auto expression = boost::make_shared<Expression>();
ClientClassDefPtr client_class = boost::make_shared<ClientClassDef>("first-class", expression);
+ client_class->setTest("substring(option[1].hex, 0, 8) == 'my-value'");
client_class->setId(1);
client_class->setModificationTime(getTimestamp("dhcp6_client_class"));
mgr.getPool()->createUpdateClientClass6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
if (hasConfigElement("dhcp6_client_class") &&
(getTimestamp("dhcp6_client_class") > lb_modification_time)) {
ASSERT_TRUE(found_class);
+ ASSERT_TRUE(found_class->getMatchExpr());
+ EXPECT_GT(found_class->getMatchExpr()->size(), 0);
EXPECT_EQ("first-class", found_class->getName());
} else {
EXPECT_EQ("cc4", depend);
}
+// Tests that match expressions are set for all client classes in the
+// dictionary.
+TEST(ClientClassDictionary, initMatchExpr) {
+ ClientClassDictionaryPtr dictionary(new ClientClassDictionary());
+ ExpressionPtr expr;
+ CfgOptionPtr cfg_option;
+
+ // Add several classes.
+ ASSERT_NO_THROW(dictionary->addClass("foo", expr, "", false,
+ false, cfg_option));
+ ASSERT_NO_THROW(dictionary->addClass("bar", expr, "member('KNOWN') or member('foo')", false,
+ false, cfg_option));
+ ASSERT_NO_THROW(dictionary->addClass("baz", expr, "substring(option[61].hex,0,3) == 'foo'", false,
+ false, cfg_option));
+
+ // Create match expressions for all of them.
+ ASSERT_NO_THROW(dictionary->initMatchExpr(AF_INET));
+
+ // Ensure that the expressions were created.
+ auto classes = *(dictionary->getClasses());
+ EXPECT_TRUE(classes[0]->getMatchExpr());
+ EXPECT_EQ(0, classes[0]->getMatchExpr()->size());
+
+ EXPECT_TRUE(classes[1]->getMatchExpr());
+ EXPECT_EQ(3, classes[1]->getMatchExpr()->size());
+
+ EXPECT_TRUE(classes[2]->getMatchExpr());
+ EXPECT_EQ(6, classes[2]->getMatchExpr()->size());
+}
+
+// Tests that an error is returned when any of the test expressions is
+// invalid, and that no expressions are initialized if there is an error
+// for a single expresion.
+TEST(ClientClassDictionary, initMatchExprError) {
+ ClientClassDictionaryPtr dictionary(new ClientClassDictionary());
+ ExpressionPtr expr;
+ CfgOptionPtr cfg_option;
+
+ // Add several classes. One of them has invalid test expression.
+ ASSERT_NO_THROW(dictionary->addClass("foo", expr, "member('KNOWN')", false,
+ false, cfg_option));
+ ASSERT_NO_THROW(dictionary->addClass("bar", expr, "wrong expression", false,
+ false, cfg_option));
+ ASSERT_NO_THROW(dictionary->addClass("baz", expr, "substring(option[61].hex,0,3) == 'foo'", false,
+ false, cfg_option));
+
+ // An attempt to initialize match expressions should fail because the
+ // test expression for the second class is invalid.
+ ASSERT_THROW(dictionary->initMatchExpr(AF_INET), std::exception);
+
+ // Ensure that no classes have their match expressions modified.
+ for (auto c : (*dictionary->getClasses())) {
+ EXPECT_FALSE(c->getMatchExpr());
+ }
+}
+
// Tests the default constructor regarding fixed fields
TEST(ClientClassDef, fixedFieldsDefaults) {
boost::scoped_ptr<ClientClassDef> cclass;