}
]
},
-
+ { "item_name": "client-classes",
+ "item_type": "list",
+ "item_optional": true,
+ "item_default": [],
+ "list_item_spec":
+ {
+ "item_name": "client-class",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "name",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ },
+ { "item_name": "test",
+ "item_type": "string",
+ "item_optional": true,
+ "item_default": ""
+ },
+ { "item_name": "option-data",
+ "item_type": "list",
+ "item_optional": true,
+ "item_default": [],
+ "list_item_spec":
+ {
+ "item_name": "single-option-data",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ {
+ "item_name": "name",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ },
+ {
+ "item_name": "code",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0
+ },
+ {
+ "item_name": "data",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ },
+ { "item_name": "csv-format",
+ "item_type": "boolean",
+ "item_optional": false,
+ "item_default": false
+ },
+ { "item_name": "space",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "dhcp4"
+ } ]
+ }
+ }
+ ]
+ }
+ },
{ "item_name": "subnet4",
"item_type": "list",
"item_optional": false,
#include <dhcp/option_definition.h>
#include <dhcpsrv/cfg_option.h>
#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>
parser = new ControlSocketParser(config_id);
} else if (config_id.compare("expired-leases-processing") == 0) {
parser = new ExpirationConfigParser();
+ } else if (config_id.compare("client-classes") == 0) {
+ parser = new ClientClassDefListParser(config_id, globalContext());
} else {
isc_throw(DhcpConfigError,
"unsupported global configuration parameter: "
ParserPtr option_parser;
ParserPtr iface_parser;
ParserPtr leases_parser;
+ ParserPtr client_classes_parser;
// Some of the parsers alter the state of the system in a way that can't
// easily be undone. (Or alter it in a way such that undoing the change has
// but defer the commit until everything else has committed.
hooks_parser = parser;
parser->build(config_pair.second);
+ } else if (config_pair.first == "client-classes") {
+ client_classes_parser = parser;
} else {
// Those parsers should be started before other
// parsers so we can call build straight away.
option_parser->commit();
}
+ // The class definitions parser is the next one to be run.
+ std::map<std::string, ConstElementPtr>::const_iterator cc_config =
+ values_map.find("client-classes");
+ if (cc_config != values_map.end()) {
+ config_pair.first = "client-classes";
+ client_classes_parser->build(cc_config->second);
+ client_classes_parser->commit();
+ }
+
// The subnet parser is the next one to be run.
std::map<std::string, ConstElementPtr>::const_iterator subnet_config =
values_map.find("subnet4");
EXPECT_EQ(0, memcmp(&data[0], exp, data.size()));
}
+// Verifies that simple list of valid classes parses and
+// is staged for commit.
+TEST_F(Dhcp4ParserTest, validClientClassDictionary) {
+ string config = "{ " + genIfaceConfig() + "," +
+ "\"valid-lifetime\": 4000, \n"
+ "\"rebind-timer\": 2000, \n"
+ "\"renew-timer\": 1000, \n"
+ "\"client-classes\" : [ \n"
+ " { \n"
+ " \"name\": \"one\" \n"
+ " }, \n"
+ " { \n"
+ " \"name\": \"two\" \n"
+ " }, \n"
+ " { \n"
+ " \"name\": \"three\" \n"
+ " } \n"
+ "], \n"
+ "\"subnet4\": [ { \n"
+ " \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ], \n"
+ " \"subnet\": \"192.0.2.0/24\" \n"
+ " } ] \n"
+ "} \n";
+
+ ConstElementPtr status;
+ ElementPtr json = Element::fromJSON(config);
+
+ EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+ ASSERT_TRUE(status);
+ checkResult(status, 0);
+
+ // We check staging config because CfgMgr::commit hasn't been executed.
+ ClientClassDictionaryPtr dictionary;
+ dictionary = CfgMgr::instance().getStagingCfg()->getClientClassDictionary();
+ ASSERT_TRUE(dictionary);
+ EXPECT_EQ(3, dictionary->getClasses()->size());
+
+ // Execute the commit
+ ASSERT_NO_THROW(CfgMgr::instance().commit());
+
+ // Verify that after commit, the current config has the correct dictionary
+ dictionary = CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
+ ASSERT_TRUE(dictionary);
+ EXPECT_EQ(3, dictionary->getClasses()->size());
+}
+
+// Verifies that an class list containing an invalid
+// class definition causes a configuraiton error.
+TEST_F(Dhcp4ParserTest, invalidClientClassDictionary) {
+ string config = "{ " + genIfaceConfig() + "," +
+ "\"valid-lifetime\": 4000, \n"
+ "\"rebind-timer\": 2000, \n"
+ "\"renew-timer\": 1000, \n"
+ "\"client-classes\" : [ \n"
+ " { \n"
+ " \"name\": \"one\", \n"
+ " \"bogus\": \"bad\" \n"
+ " } \n"
+ "], \n"
+ "\"subnet4\": [ { \n"
+ " \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ], \n"
+ " \"subnet\": \"192.0.2.0/24\" \n"
+ " } ] \n"
+ "} \n";
+
+ ConstElementPtr status;
+ ElementPtr json = Element::fromJSON(config);
+
+ EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+ ASSERT_TRUE(status);
+ checkResult(status, 1);
+}
+
+
}
/// 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.
+/// created definitions is given to the CfgMgr.
class ClientClassDefListParser : public DhcpConfigParser {
public:
/// @brief Constructor.