\"next-server\" {
switch(driver.ctx_) {
+ case isc::dhcp::Parser4Context::DHCP4:
case isc::dhcp::Parser4Context::SUBNET4:
case isc::dhcp::Parser4Context::RESERVATIONS:
case isc::dhcp::Parser4Context::CLIENT_CLASSES:
};
echo_client_id: ECHO_CLIENT_ID COLON BOOLEAN {
- ElementPtr echo(new BoolElement($3, ctx.loc2pos(@1)));
+ ElementPtr echo(new BoolElement($3, ctx.loc2pos(@3)));
ctx.stack_.back()->set("echo-client-id", echo);
};
match_client_id: MATCH_CLIENT_ID COLON BOOLEAN {
- ElementPtr match(new BoolElement($3, ctx.loc2pos(@1)));
+ ElementPtr match(new BoolElement($3, ctx.loc2pos(@3)));
ctx.stack_.back()->set("match-client-id", match);
};
next_server: NEXT_SERVER {
ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
- ElementPtr next_server(new StringElement($4, ctx.loc2pos(@1)));
+ ElementPtr next_server(new StringElement($4, ctx.loc2pos(@4)));
ctx.stack_.back()->set("next-server", next_server);
ctx.leave();
};
server_hostname: SERVER_HOSTNAME {
ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
- ElementPtr srv(new StringElement($4, ctx.loc2pos(@1)));
+ ElementPtr srv(new StringElement($4, ctx.loc2pos(@4)));
ctx.stack_.back()->set("server-hostname", srv);
ctx.leave();
};
boot_file_name: BOOT_FILE_NAME {
ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
- ElementPtr bootfile(new StringElement($4, ctx.loc2pos(@1)));
+ ElementPtr bootfile(new StringElement($4, ctx.loc2pos(@4)));
ctx.stack_.back()->set("boot-file-name", bootfile);
ctx.leave();
};
ip_address: IP_ADDRESS {
ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
- ElementPtr addr(new StringElement($4, ctx.loc2pos(@1)));
+ ElementPtr addr(new StringElement($4, ctx.loc2pos(@4)));
ctx.stack_.back()->set("ip-address", addr);
ctx.leave();
};
" \"name\": \"pxe4\","
" \"test\": \"option[93].hex == 0x0001\","
" \"boot-file-name\": \"ipxe.efi\""
- "},"
- "],"
+ "}],"
"\"subnet4\": [ { "
" \"subnet\": \"10.0.0.0/24\", "
" \"id\": 1,"
#include "marker_file.h"
#include "test_libraries.h"
#include "test_data_files_config.h"
+#include "dhcp4_test_utils.h"
#include <boost/foreach.hpp>
#include <boost/scoped_ptr.hpp>
params["space"] = DHCP4_OPTION_SPACE;
params["code"] = "56";
params["data"] = "ABCDEF0105";
- params["csv-format"] = "False";
+ params["csv-format"] = "false";
} else if (parameter == "space") {
params["name"] = "dhcp-message";
params["space"] = param_value;
params["code"] = "56";
params["data"] = "ABCDEF0105";
- params["csv-format"] = "False";
+ params["csv-format"] = "false";
} else if (parameter == "code") {
params["name"] = "dhcp-message";
params["space"] = DHCP4_OPTION_SPACE;
params["code"] = param_value;
params["data"] = "ABCDEF0105";
- params["csv-format"] = "False";
+ params["csv-format"] = "false";
} else if (parameter == "data") {
params["name"] = "dhcp-message";
params["space"] = DHCP4_OPTION_SPACE;
params["code"] = "56";
params["data"] = param_value;
- params["csv-format"] = "False";
+ params["csv-format"] = "false";
} else if (parameter == "csv-format") {
params["name"] = "dhcp-message";
params["space"] = DHCP4_OPTION_SPACE;
const std::string& parameter) {
ConstElementPtr x;
std::string config = createConfigWithOption(param_value, parameter);
- ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr json = parseDHCP4(config);
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
checkResult(x, 1);
EXPECT_TRUE(errorContainsPosition(x, "<string>"));
testInvalidOptionParam(const std::map<std::string, std::string>& params) {
ConstElementPtr x;
std::string config = createConfigWithOption(params);
- ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr json = parseDHCP4(config);
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
checkResult(x, 1);
EXPECT_TRUE(errorContainsPosition(x, "<string>"));
bool
executeConfiguration(const std::string& config, const char* operation) {
CfgMgr::instance().clear();
+ ConstElementPtr json;
ConstElementPtr status;
try {
- ElementPtr json = Element::fromJSON(config);
+ json = parseJSON(config);
status = configureDhcp4Server(*srv_, json);
} catch (const std::exception& ex) {
ADD_FAILURE() << "Unable to " << operation << ". "
// config update.
TEST_F(Dhcp4ParserTest, version) {
- ConstElementPtr x;
-
- EXPECT_NO_THROW(x = configureDhcp4Server(*srv_,
- Element::fromJSON("{\"version\": 0}")));
+ ConstElementPtr json;
+ ASSERT_NO_THROW(json = parseDHCP4("{\"version\": 0}"));
+ ConstElementPtr x;
+ EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
// returned value must be 0 (configuration accepted)
checkResult(x, 0);
}
ConstElementPtr x;
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_,
- Element::fromJSON("{\"bogus\": 5}")));
+ parseJSON("{\"bogus\": 5}")));
// returned value must be 1 (configuration parse error)
checkResult(x, 1);
+
+ // it should be refused by syntax too
+ EXPECT_THROW(parseDHCP4("{\"bogus\": 5}"), Dhcp4ParseError);
}
/// The goal of this test is to verify if wrongly defined subnet will
/// pool definition.
TEST_F(Dhcp4ParserTest, emptySubnet) {
- ConstElementPtr status;
-
- EXPECT_NO_THROW(status = configureDhcp4Server(*srv_,
- Element::fromJSON("{ " + genIfaceConfig() + "," +
+ ConstElementPtr json;
+ EXPECT_NO_THROW(json = parseDHCP4("{ " + genIfaceConfig() + "," +
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet4\": [ ], "
- "\"valid-lifetime\": 4000 }")));
+ "\"valid-lifetime\": 4000 }"));
+
+ ConstElementPtr status;
+ EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
// returned value should be 0 (success)
checkResult(status, 0);
/// Check that the renew-timer doesn't have to be specified, in which case
/// it is marked unspecified in the Subnet.
TEST_F(Dhcp4ParserTest, unspecifiedRenewTimer) {
- ConstElementPtr status;
string config = "{ " + genIfaceConfig() + "," +
"\"rebind-timer\": 2000, "
" \"subnet\": \"192.0.2.0/24\" } ],"
"\"valid-lifetime\": 4000 }";
- ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr json;
+ ASSERT_NO_THROW(json = parseDHCP4(config));
+ ConstElementPtr status;
EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
// returned value should be 0 (success)
/// Check that the rebind-timer doesn't have to be specified, in which case
/// it is marked unspecified in the Subnet.
TEST_F(Dhcp4ParserTest, unspecifiedRebindTimer) {
- ConstElementPtr status;
string config = "{ " + genIfaceConfig() + "," +
"\"renew-timer\": 1000, "
" \"subnet\": \"192.0.2.0/24\" } ],"
"\"valid-lifetime\": 4000 }";
- ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr json;
+ ASSERT_NO_THROW(json = parseDHCP4(config));
+ ConstElementPtr status;
EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
// returned value should be 0 (success)
/// parameter timer definitions.
TEST_F(Dhcp4ParserTest, subnetGlobalDefaults) {
- ConstElementPtr status;
-
string config = "{ " + genIfaceConfig() + "," +
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
" \"subnet\": \"192.0.2.0/24\" } ],"
"\"valid-lifetime\": 4000 }";
- ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr json;
+ ASSERT_NO_THROW(json = parseDHCP4(config));
+ ConstElementPtr status;
EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
// check if returned status is OK
" } ],"
"\"valid-lifetime\": 4000 }";
- ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr json;
+ ASSERT_NO_THROW(json = parseDHCP4(config));
int cnt = 0; // Number of reconfigurations
" } ],"
"\"valid-lifetime\": 4000 }";
- ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr json;
+ ASSERT_NO_THROW(json = parseDHCP4(config));
int cnt = 0; // Number of reconfigurations
do {
" } ],"
"\"valid-lifetime\": 4000 }";
- ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr json;
+ ASSERT_NO_THROW(json = parseDHCP4(config));
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
checkResult(x, 1);
// CASE 1: Configure 4 subnets, then reconfigure and remove the
// last one.
- ElementPtr json = Element::fromJSON(config4);
+ ConstElementPtr json;
+ ASSERT_NO_THROW(json = parseDHCP4(config4));
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
checkResult(x, 0);
CfgMgr::instance().clear();
// Do the reconfiguration (the last subnet is removed)
- json = Element::fromJSON(config_first3);
+ ASSERT_NO_THROW(json = parseDHCP4(config_first3));
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
checkResult(x, 0);
/// from in between (not first, not last)
/// @todo: Uncomment subnet removal test as part of #3281.
- json = Element::fromJSON(config4);
+ ASSERT_NO_THROW(json = parseDHCP4(config4));
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
checkResult(x, 0);
CfgMgr::instance().clear();
// Do reconfiguration
- json = Element::fromJSON(config_second_removed);
+ ASSERT_NO_THROW(json = parseDHCP4(config_second_removed));
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
checkResult(x, 0);
// consideration.
TEST_F(Dhcp4ParserTest, nextServerGlobal) {
- ConstElementPtr status;
-
string config = "{ " + genIfaceConfig() + "," +
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
" \"subnet\": \"192.0.2.0/24\" } ],"
"\"valid-lifetime\": 4000 }";
- ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr json;
+ ASSERT_NO_THROW(json = parseDHCP4(config));
+ ConstElementPtr status;
EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
// check if returned status is OK
// consideration.
TEST_F(Dhcp4ParserTest, nextServerSubnet) {
- ConstElementPtr status;
-
string config = "{ " + genIfaceConfig() + "," +
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
" \"subnet\": \"192.0.2.0/24\" } ],"
"\"valid-lifetime\": 4000 }";
- ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr json;
+ ASSERT_NO_THROW(json = parseDHCP4(config));
+ ConstElementPtr status;
EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
// check if returned status is OK
TEST_F(Dhcp4ParserTest, nextServerNegative) {
IfaceMgrTestConfig test_config(true);
- ConstElementPtr status;
-
// Config with junk instead of next-server address
string config_bogus1 = "{ " + genIfaceConfig() + "," +
"\"rebind-timer\": 2000, "
" \"subnet\": \"192.0.2.0/24\" } ],"
"\"valid-lifetime\": 4000 }";
- ElementPtr json1 = Element::fromJSON(config_bogus1);
- ElementPtr json2 = Element::fromJSON(config_bogus2);
- ElementPtr json3 = Element::fromJSON(config_bogus3);
+ ConstElementPtr json1;
+ ASSERT_NO_THROW(json1 = parseDHCP4(config_bogus1));
+ ConstElementPtr json2;
+ ASSERT_NO_THROW(json2 = parseDHCP4(config_bogus2));
+ ConstElementPtr json3;
+ ASSERT_NO_THROW(json3 = parseDHCP4(config_bogus3));
// check if returned status is always a failure
+ ConstElementPtr status;
EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json1));
checkResult(status, 1);
EXPECT_TRUE(errorContainsPosition(status, "<string>"));
// specific value.
TEST_F(Dhcp4ParserTest, nextServerOverride) {
- ConstElementPtr status;
-
string config = "{ " + genIfaceConfig() + "," +
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
" \"subnet\": \"192.0.2.0/24\" } ],"
"\"valid-lifetime\": 4000 }";
- ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr json;
+ ASSERT_NO_THROW(json = parseDHCP4(config));
+ ConstElementPtr status;
EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
// check if returned status is OK
" \"name\": \"foo\","
" \"code\": 100,"
" \"type\": \"uint32\","
- " \"array\": True,"
+ " \"array\": true,"
" \"space\": \"isc\""
" } ]"
"}";
" \"name\": \"foo\","
" \"code\": 100,"
" \"type\": \"uint32\","
- " \"array\": True,"
+ " \"array\": true,"
" \"space\": \"isc\","
" \"encapsulate\": \"valid-space-name\""
" } ]"
"\"option-data\": [ {"
" \"name\": \"dhcp-message\","
" \"data\": \"ABCDEF0105\","
- " \"csv-format\": False"
+ " \"csv-format\": false"
" },"
" {"
" \"name\": \"default-ip-ttl\","
" \"data\": \"01\","
- " \"csv-format\": False"
+ " \"csv-format\": false"
" } ],"
"\"subnet4\": [ { "
" \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
" \"option-data\": [ {"
" \"name\": \"dhcp-message\","
" \"data\": \"ABCDEF0105\","
- " \"csv-format\": False"
+ " \"csv-format\": false"
" },"
" {"
" \"name\": \"default-ip-ttl\","
" \"data\": \"01\","
- " \"csv-format\": False"
+ " \"csv-format\": false"
" } ]"
" } ],"
"\"valid-lifetime\": 4000 }";
"\"option-data\": [ {"
" \"name\": \"dhcp-message\","
" \"data\": \"ABCDEF0105\","
- " \"csv-format\": False"
+ " \"csv-format\": false"
" },"
" {"
" \"name\": \"foo\","
"\"option-data\": [ {"
" \"name\": \"dhcp-message\","
" \"data\": \"AB\","
- " \"csv-format\": False"
+ " \"csv-format\": false"
" } ],"
"\"subnet4\": [ { "
" \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
" \"option-data\": [ {"
" \"name\": \"dhcp-message\","
" \"data\": \"ABCDEF0105\","
- " \"csv-format\": False"
+ " \"csv-format\": false"
" },"
" {"
" \"name\": \"default-ip-ttl\","
" \"data\": \"01\","
- " \"csv-format\": False"
+ " \"csv-format\": false"
" } ]"
" } ],"
"\"valid-lifetime\": 4000 }";
" \"option-data\": [ {"
" \"name\": \"dhcp-message\","
" \"data\": \"0102030405060708090A\","
- " \"csv-format\": False"
+ " \"csv-format\": false"
" } ]"
" },"
" {"
" \"option-data\": [ {"
" \"name\": \"default-ip-ttl\","
" \"data\": \"FF\","
- " \"csv-format\": False"
+ " \"csv-format\": false"
" } ]"
" } ],"
"\"valid-lifetime\": 4000 }";
params["code"] = "41";
// Specify option values in a CSV (user friendly) format.
params["data"] = "192.0.2.10, 192.0.2.1, 192.0.2.3";
- params["csv-format"] = "True";
+ params["csv-format"] = "true";
std::string config = createConfigWithOption(params);
ElementPtr json = Element::fromJSON(config);
"\"renew-timer\": 1000,"
"\"option-data\": [ {"
" \"name\": \"vendor-encapsulated-options\","
- " \"csv-format\": False"
+ " \"csv-format\": false"
" },"
" {"
" \"name\": \"foo\","
" \"space\": \"vendor-encapsulated-options-space\","
" \"code\": 2,"
" \"data\": \"192.168.2.1\","
- " \"csv-format\": True"
+ " \"csv-format\": true"
" } ],"
"\"option-def\": [ {"
" \"name\": \"foo\","
" \"space\": \"vendor-4491\"," // VENDOR_ID_CABLE_LABS = 4491
" \"code\": 100," // just a random code
" \"data\": \"ABCDEF0105\","
- " \"csv-format\": False"
+ " \"csv-format\": false"
" },"
" {"
" \"name\": \"option-two\","
" \"space\": \"vendor-1234\","
" \"code\": 100,"
" \"data\": \"1234\","
- " \"csv-format\": False"
+ " \"csv-format\": false"
" } ],"
"\"subnet4\": [ { "
" \"pools\": [ { \"pool\": \"192.0.2.1-192.0.2.10\" } ],"
"\"option-data\": [ {"
" \"name\": \"dhcp-message\","
" \"data\": \"ABCDEF0105\","
- " \"csv-format\": False"
+ " \"csv-format\": false"
" },"
" {"
" \"name\": \"foo\","
#include <config/command_mgr.h>
#include <dhcp/dhcp4.h>
#include <dhcp4/ctrl_dhcp4_srv.h>
+#include <dhcp4/tests/dhcp4_test_utils.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/lease.h>
#include <dhcpsrv/lease_mgr_factory.h>
ASSERT_NO_THROW(server_.reset(new NakedControlledDhcpv4Srv()));
- ConstElementPtr config = Element::fromJSON(config_txt);
+ ConstElementPtr config;
+ ASSERT_NO_THROW(config = parseDHCP4(config_txt));
ConstElementPtr answer = server_->processConfig(config);
ASSERT_TRUE(answer);
" \"space\": \"vendor-4491\","
" \"code\": 2,"
" \"data\": \"10.253.175.16\","
- " \"csv-format\": True"
+ " \"csv-format\": true"
" }],"
"\"subnet4\": [ { "
" \"pools\": [ { \"pool\": \"10.254.226.0/25\" } ],"
" } ],"
"\"valid-lifetime\": 4000 }";
- ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr json;
+ ASSERT_NO_THROW(json = parseDHCP4(config));
ConstElementPtr status;
// Configure the server and make sure the config is accepted
" \"subnet\": \"192.0.2.0/24\" } ],"
"\"valid-lifetime\": 4000 }";
- ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr json;
+ ASSERT_NO_THROW(json = parseDHCP4(config, true));
EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
" \"subnet\": \"192.0.2.0/24\" } ],"
"\"valid-lifetime\": 4000 }";
- ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr json;
+ ASSERT_NO_THROW(json = parseDHCP4(config, true));
EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
" \"space\": \"vendor-4491\","
" \"code\": 2,"
" \"data\": \"192.0.2.1, 192.0.2.2\","
- " \"csv-format\": True"
+ " \"csv-format\": true"
" }],"
"\"subnet4\": [ { "
" \"pools\": [ { \"pool\": \"192.0.2.0/25\" } ],"
" } ],"
"\"valid-lifetime\": 4000 }";
- ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr json;
+ ASSERT_NO_THROW(json = parseDHCP4(config));
EXPECT_NO_THROW(x = configureDhcp4Server(srv, json));
ASSERT_TRUE(x);
" \"code\": ";
string config_postfix = ","
" \"data\": \"192.0.2.1\","
- " \"csv-format\": True"
+ " \"csv-format\": true"
" }],"
"\"subnet4\": [ { "
" \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.50\" } ],"
// definition, the config should fail.
string config_bogus = config_prefix + "99" + config_postfix;
- ElementPtr json_bogus = Element::fromJSON(config_bogus);
- ElementPtr json_valid = Element::fromJSON(config_valid);
+ ConstElementPtr json_bogus;
+ ASSERT_NO_THROW(json_bogus = parseDHCP4(config_bogus));
+ ConstElementPtr json_valid;
+ ASSERT_NO_THROW(json_valid = parseDHCP4(config_valid));
NakedDhcpv4Srv srv(0);
" \"data\": \"true\" } ], "
" \"test\": \"option[12].text == 'foo'\" } ] }";
- ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr json;
+ ASSERT_NO_THROW(json = parseDHCP4(config));
ConstElementPtr status;
// Configure the server and make sure the config is accepted
"{ \"name\": \"router\", "
" \"test\": \"option[host-name].text == 'foo'\" } ] }";
- ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr json;
+ ASSERT_NO_THROW(json = parseDHCP4(config));
ConstElementPtr status;
// Configure the server and make sure the config is accepted
" \"code\": 250, "
" \"type\": \"string\" } ] }";
- ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr json;
+ ASSERT_NO_THROW(json = parseDHCP4(config));
ConstElementPtr status;
// Configure the server and make sure the config is accepted
" \"data\": \"true\" } ], "
" \"test\": \"option[12].text == 'foo'\" } ] }";
- ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr json;
+ ASSERT_NO_THROW(json = parseDHCP4(config));
ConstElementPtr status;
// Configure the server and make sure the config is accepted
" { \"name\": \"ip-forwarding\", "
" \"data\": \"true\" } ] }";
- ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr json;
+ ASSERT_NO_THROW(json = parseDHCP4(config));
ConstElementPtr status;
// Configure the server and make sure the config is accepted
" \"data\": \"true\" } ], "
" \"test\": \"option[12].text == 'foo'\" } ] }";
- ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr json;
+ ASSERT_NO_THROW(json = parseDHCP4(config));
ConstElementPtr status;
// Configure the server and make sure the config is accepted
void
Dhcpv4SrvTest::configure(const std::string& config, NakedDhcpv4Srv& srv,
const bool commit) {
- ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr json;
+ ASSERT_NO_THROW(json = parseJSON(config));
ConstElementPtr status;
// Configure the server and make sure the config is accepted
#include <dhcpsrv/lease.h>
#include <dhcpsrv/lease_mgr_factory.h>
#include <dhcp4/dhcp4_srv.h>
+#include <dhcp4/parser_context.h>
#include <asiolink/io_address.h>
#include <cc/command_interpreter.h>
#include <list>
NakedDhcpv4Srv srv_;
};
+/// @brief Runs parser in JSON mode, useful for parser testing
+///
+/// @param in string to be parsed
+/// @return ElementPtr structure representing parsed JSON
+inline isc::data::ConstElementPtr
+parseJSON(const std::string& in)
+{
+ isc::dhcp::Parser4Context ctx;
+ return (ctx.parseString(in, isc::dhcp::Parser4Context::PARSER_JSON));
+}
+
+/// @brief Runs parser in Dhcp4 mode
+///
+/// This is a simplified Dhcp4 mode, so no outer { } and "Dhcp4" is
+/// needed. This format is used by most of the tests.
+///
+/// @param in string to be parsed
+/// @param verbose display the exception message when it fails
+/// @return ElementPtr structure representing parsed JSON
+inline isc::data::ConstElementPtr
+parseDHCP4(const std::string& in, bool verbose = false)
+{
+ try {
+ isc::dhcp::Parser4Context ctx;
+ return (ctx.parseString(in, isc::dhcp::Parser4Context::SUBPARSER_DHCP4));
+ }
+ catch (const std::exception& ex) {
+ if (verbose) {
+ std::cout << "EXCEPTION: " << ex.what() << std::endl;
+ }
+ throw;
+ }
+}
+
+/// @brief Runs parser in option definition mode
+///
+/// This function parses specified text as JSON that defines option definitions.
+///
+/// @param in string to be parsed
+/// @param verbose display the exception message when it fails
+/// @return ElementPtr structure representing parsed JSON
+inline isc::data::ConstElementPtr
+parseOPTION_DEF(const std::string& in, bool verbose = false)
+{
+ try {
+ isc::dhcp::Parser4Context ctx;
+ return (ctx.parseString(in, isc::dhcp::Parser4Context::PARSER_OPTION_DEF));
+ }
+ catch (const std::exception& ex) {
+ if (verbose) {
+ std::cout << "EXCEPTION: " << ex.what() << std::endl;
+ }
+ throw;
+ }
+}
+
}; // end of isc::dhcp::test namespace
}; // end of isc::dhcp namespace
}; // end of isc namespace
" } ],"
"\"valid-lifetime\": 4000 }";
- ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr json;
+ EXPECT_NO_THROW(json = parseDHCP4(config));
ConstElementPtr status;
// Configure the server and make sure the config is accepted
" } ],"
"\"valid-lifetime\": 4000 }";
- ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr json;
+ EXPECT_NO_THROW(json = parseDHCP4(config));
ConstElementPtr status;
// Configure the server and make sure the config is accepted
#include <dhcp/hwaddr.h>
#include <dhcp/iface_mgr.h>
#include <dhcp4/ctrl_dhcp4_srv.h>
+#include <dhcp4/parser_context.h>
#include <dhcp4/tests/dhcp4_test_utils.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/lease.h>
LeaseMgrFactory::destroy();
isc::log::setDefaultLoggingOutput();
static_cast<void>(remove(TEST_FILE));
+ static_cast<void>(remove(TEST_INCLUDE));
};
/// @brief writes specified content to a well known file
/// Writes specified content to TEST_FILE. Tests will
/// attempt to read that file.
///
+ /// @param file_name name of file to be written
/// @param content content to be written to file
- void writeFile(const std::string& content) {
- static_cast<void>(remove(TEST_FILE));
+ void writeFile(const std::string& file_name, const std::string& content) {
+ static_cast<void>(remove(file_name.c_str()));
- ofstream out(TEST_FILE, ios::trunc);
+ ofstream out(file_name.c_str(), ios::trunc);
EXPECT_TRUE(out.is_open());
out << content;
out.close();
/// Name of a config file used during tests
static const char* TEST_FILE;
+ static const char* TEST_INCLUDE;
};
const char* JSONFileBackendTest::TEST_FILE = "test-config.json";
+const char* JSONFileBackendTest::TEST_INCLUDE = "test-include.json";
// This test checks if configuration can be read from a JSON file.
TEST_F(JSONFileBackendTest, jsonFile) {
"\"valid-lifetime\": 4000 }"
"}";
- writeFile(config);
+ writeFile(TEST_FILE, config);
// Now initialize the server
boost::scoped_ptr<ControlledDhcpv4Srv> srv;
EXPECT_EQ(Lease::TYPE_V4, pools3.at(0)->getType());
}
-// This test checks if configuration can be read from a JSON file.
-TEST_F(JSONFileBackendTest, comments) {
+// This test checks if configuration can be read from a JSON file
+// using hash (#) line comments
+TEST_F(JSONFileBackendTest, hashComments) {
string config_hash_comments = "# This is a comment. It should be \n"
"#ignored. Real config starts in line below\n"
"\"valid-lifetime\": 4000 }"
"}";
- /// @todo: Implement C++-style (// ...) comments
- /// @todo: Implement C-style (/* ... */) comments
+ writeFile(TEST_FILE, config_hash_comments);
+
+ // Now initialize the server
+ boost::scoped_ptr<ControlledDhcpv4Srv> srv;
+ ASSERT_NO_THROW(
+ srv.reset(new ControlledDhcpv4Srv(0))
+ );
+
+ // And configure it using config with comments.
+ EXPECT_NO_THROW(srv->init(TEST_FILE));
+
+ // Now check if the configuration has been applied correctly.
+ const Subnet4Collection* subnets =
+ CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
+ ASSERT_TRUE(subnets);
+ ASSERT_EQ(1, subnets->size());
+
+ // Check subnet 1.
+ EXPECT_EQ("192.0.2.0", subnets->at(0)->get().first.toText());
+ EXPECT_EQ(22, subnets->at(0)->get().second);
+
+ // Check pools in the first subnet.
+ const PoolCollection& pools1 = subnets->at(0)->getPools(Lease::TYPE_V4);
+ ASSERT_EQ(1, pools1.size());
+ EXPECT_EQ("192.0.2.0", pools1.at(0)->getFirstAddress().toText());
+ EXPECT_EQ("192.0.2.255", pools1.at(0)->getLastAddress().toText());
+ EXPECT_EQ(Lease::TYPE_V4, pools1.at(0)->getType());
+}
- writeFile(config_hash_comments);
+// This test checks if configuration can be read from a JSON file
+// using C++ line (//) comments.
+TEST_F(JSONFileBackendTest, cppLineComments) {
+
+ string config_cpp_line_comments = "// This is a comment. It should be \n"
+ "//ignored. Real config starts in line below\n"
+ "{ \"Dhcp4\": {"
+ "\"interfaces-config\": {"
+ " \"interfaces\": [ \"*\" ]"
+ "},"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, \n"
+ "// comments in the middle should be ignored, too\n"
+ "\"subnet4\": [ { "
+ " \"pools\": [ { \"pool\": \"192.0.2.0/24\" } ],"
+ " \"subnet\": \"192.0.2.0/22\" "
+ " } ],"
+ "\"valid-lifetime\": 4000 }"
+ "}";
+
+ writeFile(TEST_FILE, config_cpp_line_comments);
// Now initialize the server
boost::scoped_ptr<ControlledDhcpv4Srv> srv;
EXPECT_EQ(Lease::TYPE_V4, pools1.at(0)->getType());
}
+// This test checks if configuration can be read from a JSON file
+// using C block (/* */) comments
+TEST_F(JSONFileBackendTest, cBlockComments) {
+
+ string config_c_block_comments = "/* This is a comment. It should be \n"
+ "ignored. Real config starts in line below*/\n"
+ "{ \"Dhcp4\": {"
+ "\"interfaces-config\": {"
+ " \"interfaces\": [ \"*\" ]"
+ "},"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, \n"
+ "/* comments in the middle should be ignored, too*/\n"
+ "\"subnet4\": [ { "
+ " \"pools\": [ { \"pool\": \"192.0.2.0/24\" } ],"
+ " \"subnet\": \"192.0.2.0/22\" "
+ " } ],"
+ "\"valid-lifetime\": 4000 }"
+ "}";
+
+ writeFile(TEST_FILE, config_c_block_comments);
+
+ // Now initialize the server
+ boost::scoped_ptr<ControlledDhcpv4Srv> srv;
+ ASSERT_NO_THROW(
+ srv.reset(new ControlledDhcpv4Srv(0))
+ );
+
+ // And configure it using config with comments.
+ EXPECT_NO_THROW(srv->init(TEST_FILE));
+
+ // Now check if the configuration has been applied correctly.
+ const Subnet4Collection* subnets =
+ CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
+ ASSERT_TRUE(subnets);
+ ASSERT_EQ(1, subnets->size());
+
+ // Check subnet 1.
+ EXPECT_EQ("192.0.2.0", subnets->at(0)->get().first.toText());
+ EXPECT_EQ(22, subnets->at(0)->get().second);
+
+ // Check pools in the first subnet.
+ const PoolCollection& pools1 = subnets->at(0)->getPools(Lease::TYPE_V4);
+ ASSERT_EQ(1, pools1.size());
+ EXPECT_EQ("192.0.2.0", pools1.at(0)->getFirstAddress().toText());
+ EXPECT_EQ("192.0.2.255", pools1.at(0)->getLastAddress().toText());
+ EXPECT_EQ(Lease::TYPE_V4, pools1.at(0)->getType());
+}
+
+// This test checks if configuration can be read from a JSON file
+// using an include file.
+TEST_F(JSONFileBackendTest, include) {
+
+ string config_hash_comments = "{ \"Dhcp4\": {"
+ "\"interfaces-config\": {"
+ " \"interfaces\": [ \"*\" ]"
+ "},"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, \n"
+ "<?include \"" + string(TEST_INCLUDE) + "\"?>,"
+ "\"valid-lifetime\": 4000 }"
+ "}";
+ string include = "\n"
+ "\"subnet4\": [ { "
+ " \"pools\": [ { \"pool\": \"192.0.2.0/24\" } ],"
+ " \"subnet\": \"192.0.2.0/22\" "
+ " } ]\n";
+
+ writeFile(TEST_FILE, config_hash_comments);
+ writeFile(TEST_INCLUDE, include);
+
+ // Now initialize the server
+ boost::scoped_ptr<ControlledDhcpv4Srv> srv;
+ ASSERT_NO_THROW(
+ srv.reset(new ControlledDhcpv4Srv(0))
+ );
+
+ // And configure it using config with comments.
+ EXPECT_NO_THROW(srv->init(TEST_FILE));
+
+ // Now check if the configuration has been applied correctly.
+ const Subnet4Collection* subnets =
+ CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
+ ASSERT_TRUE(subnets);
+ ASSERT_EQ(1, subnets->size());
+
+ // Check subnet 1.
+ EXPECT_EQ("192.0.2.0", subnets->at(0)->get().first.toText());
+ EXPECT_EQ(22, subnets->at(0)->get().second);
+
+ // Check pools in the first subnet.
+ const PoolCollection& pools1 = subnets->at(0)->getPools(Lease::TYPE_V4);
+ ASSERT_EQ(1, pools1.size());
+ EXPECT_EQ("192.0.2.0", pools1.at(0)->getFirstAddress().toText());
+ EXPECT_EQ("192.0.2.255", pools1.at(0)->getLastAddress().toText());
+ EXPECT_EQ(Lease::TYPE_V4, pools1.at(0)->getType());
+}
+
+// This test checks if recursive include of a file is detected
+TEST_F(JSONFileBackendTest, recursiveInclude) {
+
+ string config_recursive_include = "{ \"Dhcp4\": {"
+ "\"interfaces-config\": {"
+ " \"interfaces\": [ <?include \"" + string(TEST_INCLUDE) + "\"?> ]"
+ "},"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, \n"
+ "\"subnet4\": [ { "
+ " \"pools\": [ { \"pool\": \"192.0.2.0/24\" } ],"
+ " \"subnet\": \"192.0.2.0/22\" "
+ " } ],"
+ "\"valid-lifetime\": 4000 }"
+ "}";
+ string include = "\"eth\", <?include \"" + string(TEST_INCLUDE) + "\"?>";
+ string msg = "configuration error using file '" + string(TEST_FILE) +
+ "': Too many nested include.";
+
+ writeFile(TEST_FILE, config_recursive_include);
+ writeFile(TEST_INCLUDE, include);
+
+ // Now initialize the server
+ boost::scoped_ptr<ControlledDhcpv4Srv> srv;
+ ASSERT_NO_THROW(
+ srv.reset(new ControlledDhcpv4Srv(0))
+ );
+
+ // And configure it using config with comments.
+ try {
+ srv->init(TEST_FILE);
+ FAIL() << "Expected Dhcp4ParseError but nothing was raised";
+ }
+ catch (const Exception& ex) {
+ EXPECT_EQ(msg, ex.what());
+ }
+}
+
// This test checks if configuration detects failure when trying:
// - empty file
// - empty filename
EXPECT_THROW(srv->init(""), BadValue);
// Try to configure it using empty file. Should fail.
- writeFile(config_empty);
+ writeFile(TEST_FILE, config_empty);
EXPECT_THROW(srv->init(TEST_FILE), BadValue);
// Now try to load a config that does not have Dhcp4 component.
- writeFile(config_v4);
+ writeFile(TEST_FILE, config_v4);
EXPECT_THROW(srv->init(TEST_FILE), BadValue);
// Now try to load a config with Dhcp4 full of nonsense.
- writeFile(config_nonsense);
+ writeFile(TEST_FILE, config_nonsense);
EXPECT_THROW(srv->init(TEST_FILE), BadValue);
}
"\"subnet4\": [ ],"
"\"valid-lifetime\": 4000 }"
"}";
- writeFile(config);
+ writeFile(TEST_FILE, config);
// Create an instance of the server and intialize it.
boost::scoped_ptr<ControlledDhcpv4Srv> srv;
"\"subnet4\": [ ],"
"\"valid-lifetime\": 4000 }"
"}";
- writeFile(config);
+ writeFile(TEST_FILE, config);
// Create an instance of the server and intialize it.
boost::scoped_ptr<ControlledDhcpv4Srv> srv;
JSONFileBackendMySQLTest::
testBackendReconfiguration(const std::string& backend_first,
const std::string& backend_second) {
- writeFile(createConfiguration(backend_first));
+ writeFile(TEST_FILE, createConfiguration(backend_first));
// Create an instance of the server and intialize it.
boost::scoped_ptr<NakedControlledDhcpv4Srv> srv;
LeaseMgrFactory::instance().getType());
// New configuration modifies the lease database backend type.
- writeFile(createConfiguration(backend_second));
+ writeFile(TEST_FILE, createConfiguration(backend_second));
// Explicitly calling signal handler for SIGHUP to trigger server
// reconfiguration.