Commit e9198441 authored by Francis Dupont's avatar Francis Dupont
Browse files

[5351] Added client classes. Todo: update doc

parent ff211701
...@@ -5625,6 +5625,11 @@ TEST_F(Dhcp4ParserTest, comments) { ...@@ -5625,6 +5625,11 @@ TEST_F(Dhcp4ParserTest, comments) {
" \"data\": \"ABCDEF0105\",\n" " \"data\": \"ABCDEF0105\",\n"
" \"csv-format\": false\n" " \"csv-format\": false\n"
" } ],\n" " } ],\n"
"\"client-classes\": [ {\n"
" \"name\": \"all\",\n"
" \"comment\": \"match all\",\n"
" \"test\": \"'' == ''\"\n"
" } ],\n"
"\"shared-networks\": [ {\n" "\"shared-networks\": [ {\n"
" \"name\": \"foo\"\n," " \"name\": \"foo\"\n,"
" \"comment\": \"A shared network\"\n," " \"comment\": \"A shared network\"\n,"
...@@ -5683,6 +5688,23 @@ TEST_F(Dhcp4ParserTest, comments) { ...@@ -5683,6 +5688,23 @@ TEST_F(Dhcp4ParserTest, comments) {
ASSERT_TRUE(ctx_opt_desc->get("comment")); ASSERT_TRUE(ctx_opt_desc->get("comment"));
EXPECT_EQ("\"Set option value\"", ctx_opt_desc->get("comment")->str()); EXPECT_EQ("\"Set option value\"", ctx_opt_desc->get("comment")->str());
// And there is a client class.
ClientClassDictionaryPtr dict =
CfgMgr::instance().getStagingCfg()->getClientClassDictionary();
ASSERT_TRUE(dict);
EXPECT_EQ(1, dict->getClasses()->size());
ClientClassDefPtr cclass = dict->findClass("all");
ASSERT_TRUE(cclass);
EXPECT_EQ("all", cclass->getName());
EXPECT_EQ("'' == ''", cclass->getTest());
// Check client class user context.
ConstElementPtr ctx_class = cclass->getContext();
ASSERT_TRUE(ctx_class);
ASSERT_EQ(1, ctx_class->size());
ASSERT_TRUE(ctx_class->get("comment"));
EXPECT_EQ("\"match all\"", ctx_class->get("comment")->str());
// Now verify that the shared network was indeed configured. // Now verify that the shared network was indeed configured.
CfgSharedNetworks4Ptr cfg_net = CfgMgr::instance().getStagingCfg() CfgSharedNetworks4Ptr cfg_net = CfgMgr::instance().getStagingCfg()
->getCfgSharedNetworks4(); ->getCfgSharedNetworks4();
......
...@@ -1588,6 +1588,21 @@ const char* EXTRACTED_CONFIGS[] = { ...@@ -1588,6 +1588,21 @@ const char* EXTRACTED_CONFIGS[] = {
// CONFIGURATION 58 // CONFIGURATION 58
"{\n" "{\n"
" \"comment\": \"A DHCPv4 server\",\n" " \"comment\": \"A DHCPv4 server\",\n"
" \"client-classes\": [\n"
" {\n"
" \"comment\": \"match all\",\n"
" \"name\": \"all\",\n"
" \"test\": \"'' == ''\"\n"
" }\n"
" ],\n"
" \"option-data\": [\n"
" {\n"
" \"comment\": \"Set option value\",\n"
" \"csv-format\": false,\n"
" \"data\": \"ABCDEF0105\",\n"
" \"name\": \"dhcp-message\"\n"
" }\n"
" ],\n"
" \"option-def\": [\n" " \"option-def\": [\n"
" {\n" " {\n"
" \"comment\": \"An option definition\",\n" " \"comment\": \"An option definition\",\n"
...@@ -6262,6 +6277,18 @@ const char* UNPARSED_CONFIGS[] = { ...@@ -6262,6 +6277,18 @@ const char* UNPARSED_CONFIGS[] = {
// CONFIGURATION 58 // CONFIGURATION 58
"{\n" "{\n"
" \"comment\": \"A DHCPv4 server\",\n" " \"comment\": \"A DHCPv4 server\",\n"
" \"client-classes\": [\n"
" {\n"
" \"comment\": \"match all\",\n"
" \"boot-file-name\": \"\",\n"
" \"name\": \"all\",\n"
" \"next-server\": \"0.0.0.0\",\n"
" \"option-data\": [ ],\n"
" \"option-def\": [ ],\n"
" \"server-hostname\": \"\",\n"
" \"test\": \"'' == ''\"\n"
" }\n"
" ],\n"
" \"decline-probation-period\": 86400,\n" " \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n" " \"dhcp-ddns\": {\n"
" \"always-include-fqdn\": false,\n" " \"always-include-fqdn\": false,\n"
...@@ -6298,7 +6325,17 @@ const char* UNPARSED_CONFIGS[] = { ...@@ -6298,7 +6325,17 @@ const char* UNPARSED_CONFIGS[] = {
" \"lease-database\": {\n" " \"lease-database\": {\n"
" \"type\": \"memfile\"\n" " \"type\": \"memfile\"\n"
" },\n" " },\n"
" \"option-data\": [ ],\n" " \"option-data\": [\n"
" {\n"
" \"comment\": \"Set option value\",\n"
" \"always-send\": false,\n"
" \"code\": 56,\n"
" \"csv-format\": false,\n"
" \"data\": \"ABCDEF0105\",\n"
" \"name\": \"dhcp-message\",\n"
" \"space\": \"dhcp4\"\n"
" }\n"
" ],\n"
" \"option-def\": [\n" " \"option-def\": [\n"
" {\n" " {\n"
" \"comment\": \"An option definition\",\n" " \"comment\": \"An option definition\",\n"
......
...@@ -6067,6 +6067,11 @@ TEST_F(Dhcp6ParserTest, comments) { ...@@ -6067,6 +6067,11 @@ TEST_F(Dhcp6ParserTest, comments) {
" \"data\": \"ABCDEF0105\",\n" " \"data\": \"ABCDEF0105\",\n"
" \"csv-format\": false\n" " \"csv-format\": false\n"
" } ],\n" " } ],\n"
"\"client-classes\": [ {\n"
" \"name\": \"all\",\n"
" \"comment\": \"match all\",\n"
" \"test\": \"'' == ''\"\n"
" } ],\n"
"\"shared-networks\": [ {\n" "\"shared-networks\": [ {\n"
" \"name\": \"foo\"\n," " \"name\": \"foo\"\n,"
" \"comment\": \"A shared network\"\n," " \"comment\": \"A shared network\"\n,"
...@@ -6133,6 +6138,23 @@ TEST_F(Dhcp6ParserTest, comments) { ...@@ -6133,6 +6138,23 @@ TEST_F(Dhcp6ParserTest, comments) {
ASSERT_TRUE(ctx_opt_desc->get("comment")); ASSERT_TRUE(ctx_opt_desc->get("comment"));
EXPECT_EQ("\"Set option value\"", ctx_opt_desc->get("comment")->str()); EXPECT_EQ("\"Set option value\"", ctx_opt_desc->get("comment")->str());
// And there is a client class.
ClientClassDictionaryPtr dict =
CfgMgr::instance().getStagingCfg()->getClientClassDictionary();
ASSERT_TRUE(dict);
EXPECT_EQ(1, dict->getClasses()->size());
ClientClassDefPtr cclass = dict->findClass("all");
ASSERT_TRUE(cclass);
EXPECT_EQ("all", cclass->getName());
EXPECT_EQ("'' == ''", cclass->getTest());
// Check client class user context.
ConstElementPtr ctx_class = cclass->getContext();
ASSERT_TRUE(ctx_class);
ASSERT_EQ(1, ctx_class->size());
ASSERT_TRUE(ctx_class->get("comment"));
EXPECT_EQ("\"match all\"", ctx_class->get("comment")->str());
// Now verify that the shared network was indeed configured. // Now verify that the shared network was indeed configured.
CfgSharedNetworks6Ptr cfg_net = CfgMgr::instance().getStagingCfg() CfgSharedNetworks6Ptr cfg_net = CfgMgr::instance().getStagingCfg()
->getCfgSharedNetworks6(); ->getCfgSharedNetworks6();
......
...@@ -1463,6 +1463,21 @@ const char* EXTRACTED_CONFIGS[] = { ...@@ -1463,6 +1463,21 @@ const char* EXTRACTED_CONFIGS[] = {
// CONFIGURATION 51 // CONFIGURATION 51
"{\n" "{\n"
" \"comment\": \"A DHCPv6 server\",\n" " \"comment\": \"A DHCPv6 server\",\n"
" \"client-classes\": [\n"
" {\n"
" \"comment\": \"match all\",\n"
" \"name\": \"all\",\n"
" \"test\": \"'' == ''\"\n"
" }\n"
" ],\n"
" \"option-data\": [\n"
" {\n"
" \"comment\": \"Set option value\",\n"
" \"csv-format\": false,\n"
" \"data\": \"ABCDEF0105\",\n"
" \"name\": \"subscriber-id\"\n"
" }\n"
" ],\n"
" \"option-def\": [\n" " \"option-def\": [\n"
" {\n" " {\n"
" \"comment\": \"An option definition\",\n" " \"comment\": \"An option definition\",\n"
...@@ -5873,6 +5888,14 @@ const char* UNPARSED_CONFIGS[] = { ...@@ -5873,6 +5888,14 @@ const char* UNPARSED_CONFIGS[] = {
// CONFIGURATION 51 // CONFIGURATION 51
"{\n" "{\n"
" \"comment\": \"A DHCPv6 server\",\n" " \"comment\": \"A DHCPv6 server\",\n"
" \"client-classes\": [\n"
" {\n"
" \"comment\": \"match all\",\n"
" \"name\": \"all\",\n"
" \"option-data\": [ ],\n"
" \"test\": \"'' == ''\"\n"
" }\n"
" ],\n"
" \"decline-probation-period\": 86400,\n" " \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n" " \"dhcp-ddns\": {\n"
" \"always-include-fqdn\": false,\n" " \"always-include-fqdn\": false,\n"
...@@ -5909,7 +5932,17 @@ const char* UNPARSED_CONFIGS[] = { ...@@ -5909,7 +5932,17 @@ const char* UNPARSED_CONFIGS[] = {
" \"type\": \"memfile\"\n" " \"type\": \"memfile\"\n"
" },\n" " },\n"
" \"mac-sources\": [ \"any\" ],\n" " \"mac-sources\": [ \"any\" ],\n"
" \"option-data\": [ ],\n" " \"option-data\": [\n"
" {\n"
" \"comment\": \"Set option value\",\n"
" \"always-send\": false,\n"
" \"code\": 38,\n"
" \"csv-format\": false,\n"
" \"data\": \"ABCDEF0105\",\n"
" \"name\": \"subscriber-id\",\n"
" \"space\": \"dhcp6\"\n"
" }\n"
" ],\n"
" \"option-def\": [\n" " \"option-def\": [\n"
" {\n" " {\n"
" \"comment\": \"An option definition\",\n" " \"comment\": \"An option definition\",\n"
......
...@@ -132,6 +132,8 @@ ElementPtr ...@@ -132,6 +132,8 @@ ElementPtr
ClientClassDef:: toElement() const { ClientClassDef:: toElement() const {
uint16_t family = CfgMgr::instance().getFamily(); uint16_t family = CfgMgr::instance().getFamily();
ElementPtr result = Element::createMap(); ElementPtr result = Element::createMap();
// Set user-context
contextToElement(result);
// Set name // Set name
result->set("name", Element::create(name_)); result->set("name", Element::create(name_));
// Set original match expression (empty string won't parse) // Set original match expression (empty string won't parse)
...@@ -185,12 +187,14 @@ ClientClassDictionary::addClass(const std::string& name, ...@@ -185,12 +187,14 @@ ClientClassDictionary::addClass(const std::string& name,
const std::string& test, const std::string& test,
const CfgOptionPtr& cfg_option, const CfgOptionPtr& cfg_option,
CfgOptionDefPtr cfg_option_def, CfgOptionDefPtr cfg_option_def,
ConstElementPtr user_context,
asiolink::IOAddress next_server, asiolink::IOAddress next_server,
const std::string& sname, const std::string& sname,
const std::string& filename) { const std::string& filename) {
ClientClassDefPtr cclass(new ClientClassDef(name, match_expr, cfg_option)); ClientClassDefPtr cclass(new ClientClassDef(name, match_expr, cfg_option));
cclass->setTest(test); cclass->setTest(test);
cclass->setCfgOptionDef(cfg_option_def); cclass->setCfgOptionDef(cfg_option_def);
cclass->setContext(user_context),
cclass->setNextServer(next_server); cclass->setNextServer(next_server);
cclass->setSname(sname); cclass->setSname(sname);
cclass->setFilename(filename); cclass->setFilename(filename);
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#define CLIENT_CLASS_DEF_H #define CLIENT_CLASS_DEF_H
#include <cc/cfg_to_element.h> #include <cc/cfg_to_element.h>
#include <cc/user_context.h>
#include <dhcpsrv/cfg_option.h> #include <dhcpsrv/cfg_option.h>
#include <dhcpsrv/cfg_option_def.h> #include <dhcpsrv/cfg_option_def.h>
#include <eval/token.h> #include <eval/token.h>
...@@ -39,7 +40,7 @@ public: ...@@ -39,7 +40,7 @@ public:
}; };
/// @brief Embodies a single client class definition /// @brief Embodies a single client class definition
class ClientClassDef : public isc::data::CfgToElement { class ClientClassDef : public UserContext, public isc::data::CfgToElement {
public: public:
/// @brief Constructor /// @brief Constructor
/// ///
...@@ -236,6 +237,7 @@ public: ...@@ -236,6 +237,7 @@ public:
/// @param test Original version of match_expr /// @param test Original version of match_expr
/// @param options Collection of options members should be given /// @param options Collection of options members should be given
/// @param defs Option definitions (optional) /// @param defs Option definitions (optional)
/// @param user_context User context (optional)
/// @param next_server next-server value for this class (optional) /// @param next_server next-server value for this class (optional)
/// @param sname server-name value for this class (optional) /// @param sname server-name value for this class (optional)
/// @param filename boot-file-name value for this class (optional) /// @param filename boot-file-name value for this class (optional)
...@@ -246,6 +248,7 @@ public: ...@@ -246,6 +248,7 @@ public:
void addClass(const std::string& name, const ExpressionPtr& match_expr, void addClass(const std::string& name, const ExpressionPtr& match_expr,
const std::string& test, const CfgOptionPtr& options, const std::string& test, const CfgOptionPtr& options,
CfgOptionDefPtr defs = CfgOptionDefPtr(), CfgOptionDefPtr defs = CfgOptionDefPtr(),
isc::data::ConstElementPtr user_context = isc::data::ConstElementPtr(),
asiolink::IOAddress next_server = asiolink::IOAddress("0.0.0.0"), asiolink::IOAddress next_server = asiolink::IOAddress("0.0.0.0"),
const std::string& sname = std::string(), const std::string& sname = std::string(),
const std::string& filename = std::string()); const std::string& filename = std::string());
......
...@@ -127,6 +127,9 @@ ClientClassDefParser::parse(ClientClassDictionaryPtr& class_dictionary, ...@@ -127,6 +127,9 @@ ClientClassDefParser::parse(ClientClassDictionaryPtr& class_dictionary,
opts_parser.parse(options, option_data); opts_parser.parse(options, option_data);
} }
// Parse user context
ConstElementPtr user_context = class_def_cfg->get("user-context");
// Let's try to parse the next-server field // Let's try to parse the next-server field
IOAddress next_server("0.0.0.0"); IOAddress next_server("0.0.0.0");
if (class_def_cfg->contains("next-server")) { if (class_def_cfg->contains("next-server")) {
...@@ -182,8 +185,8 @@ ClientClassDefParser::parse(ClientClassDictionaryPtr& class_dictionary, ...@@ -182,8 +185,8 @@ ClientClassDefParser::parse(ClientClassDictionaryPtr& class_dictionary,
// Add the client class definition // Add the client class definition
try { try {
class_dictionary->addClass(name, match_expr, test, options, class_dictionary->addClass(name, match_expr, test, options, defs,
defs, next_server, sname, filename); user_context, next_server, sname, filename);
} catch (const std::exception& ex) { } catch (const std::exception& ex) {
isc_throw(DhcpConfigError, "Can't add class: " << ex.what() isc_throw(DhcpConfigError, "Can't add class: " << ex.what()
<< " (" << class_def_cfg->getPosition() << ")"); << " (" << class_def_cfg->getPosition() << ")");
......
...@@ -386,6 +386,9 @@ TEST(ClientClassDef, unparseDef) { ...@@ -386,6 +386,9 @@ TEST(ClientClassDef, unparseDef) {
ASSERT_NO_THROW(cclass.reset(new ClientClassDef(name, expr))); ASSERT_NO_THROW(cclass.reset(new ClientClassDef(name, expr)));
std::string test = "option[12].text == 'foo'"; std::string test = "option[12].text == 'foo'";
cclass->setTest(test); cclass->setTest(test);
std::string comment = "bar";
std::string user_context = "{ \"comment\": \"" + comment + "\" }";
cclass->setContext(isc::data::Element::fromJSON(user_context));
std::string next_server = "1.2.3.4"; std::string next_server = "1.2.3.4";
cclass->setNextServer(IOAddress(next_server)); cclass->setNextServer(IOAddress(next_server));
std::string sname = "my-server.example.com"; std::string sname = "my-server.example.com";
...@@ -395,6 +398,7 @@ TEST(ClientClassDef, unparseDef) { ...@@ -395,6 +398,7 @@ TEST(ClientClassDef, unparseDef) {
// Unparse it // Unparse it
std::string expected = "{\n" std::string expected = "{\n"
"\"comment\": \"" + comment + "\",\n"
"\"name\": \"" + name + "\",\n" "\"name\": \"" + name + "\",\n"
"\"test\": \"" + test + "\",\n" "\"test\": \"" + test + "\",\n"
"\"next-server\": \"" + next_server + "\",\n" "\"next-server\": \"" + next_server + "\",\n"
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment