From: Razvan Becheriu Date: Tue, 23 Jul 2019 16:51:33 +0000 (+0300) Subject: [#729,!434] fixed missing commit of runtime option defs on v4 X-Git-Tag: Kea-1.7.0~37 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cc990f88768413448403f54c0e28e79b1cec11de;p=thirdparty%2Fkea.git [#729,!434] fixed missing commit of runtime option defs on v4 --- diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.cc b/src/bin/dhcp4/ctrl_dhcp4_srv.cc index 4987613534..591427c549 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.cc +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.cc @@ -8,13 +8,14 @@ #include #include #include +#include +#include +#include #include #include #include -#include #include -#include -#include +#include #include #include #include @@ -22,11 +23,11 @@ #include #include -using namespace isc::data; +using namespace isc::config; using namespace isc::db; +using namespace isc::data; using namespace isc::dhcp; using namespace isc::hooks; -using namespace isc::config; using namespace isc::stats; using namespace std; @@ -746,6 +747,10 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) { } } + // Finally, we can commit runtime option definitions in libdhcp++. This is + // exception free. + LibDHCP::commitRuntimeOptionDefs(); + // This hook point notifies hooks libraries that the configuration of the // DHCPv4 server has completed. It provides the hook library with the pointer // to the common IO service object, new server configuration in the JSON diff --git a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc index 14439e52a2..30d00cc791 100644 --- a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc @@ -12,11 +12,12 @@ #include #include #include -#include -#include +#include #include #include #include +#include +#include #include #include #include @@ -136,7 +137,7 @@ public: } void createUnixChannelServer() { - ::remove(socket_path_.c_str()); + static_cast(::remove(socket_path_.c_str())); // Just a simple config. The important part here is the socket // location information. @@ -179,7 +180,6 @@ public: // changed. CfgMgr::instance().commit(); - ASSERT_TRUE(answer); int status = 0; @@ -208,7 +208,7 @@ public: CfgMgr::instance().clear(); // Remove unix socket file - ::remove(socket_path_.c_str()); + static_cast(::remove(socket_path_.c_str())); } /// @brief Conducts a command/response exchange via UnixCommandSocket @@ -412,7 +412,6 @@ TEST_F(CtrlChannelDhcpv4SrvTest, commands) { } // Check that the "libreload" command will reload libraries - TEST_F(CtrlChannelDhcpv4SrvTest, libreload) { createUnixChannelServer(); @@ -427,9 +426,9 @@ TEST_F(CtrlChannelDhcpv4SrvTest, libreload) { HooksManager::loadLibraries(libraries); // Check they are loaded. - std::vector loaded_libraries = - HooksManager::getLibraryNames(); - ASSERT_TRUE(extractNames(libraries) == loaded_libraries); + HookLibsCollection loaded_libraries = + HooksManager::getLibraryInfo(); + ASSERT_TRUE(libraries == loaded_libraries); // ... which also included checking that the marker file created by the // load functions exists and holds the correct value (of "12" - the @@ -453,303 +452,188 @@ TEST_F(CtrlChannelDhcpv4SrvTest, libreload) { EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "1212")); } -// This test checks which commands are registered by the DHCPv4 server. -TEST_F(CtrlChannelDhcpv4SrvTest, commandsRegistration) { +// Check that the "config-set" command will replace current configuration +TEST_F(CtrlChannelDhcpv4SrvTest, configSet) { + createUnixChannelServer(); - ConstElementPtr list_cmds = createCommand("list-commands"); - ConstElementPtr answer; + // Define strings to permutate the config arguments + // (Note the line feeds makes errors easy to find) + string set_config_txt = "{ \"command\": \"config-set\" \n"; + string args_txt = " \"arguments\": { \n"; + string dhcp4_cfg_txt = + " \"Dhcp4\": { \n" + " \"interfaces-config\": { \n" + " \"interfaces\": [\"*\"] \n" + " }, \n" + " \"valid-lifetime\": 4000, \n" + " \"renew-timer\": 1000, \n" + " \"rebind-timer\": 2000, \n" + " \"lease-database\": { \n" + " \"type\": \"memfile\", \n" + " \"persist\":false, \n" + " \"lfc-interval\": 0 \n" + " }, \n" + " \"expired-leases-processing\": { \n" + " \"reclaim-timer-wait-time\": 0, \n" + " \"hold-reclaimed-time\": 0, \n" + " \"flush-reclaimed-timer-wait-time\": 0 \n" + " }," + " \"subnet4\": [ \n"; + string subnet1 = + " {\"subnet\": \"192.2.0.0/24\", \n" + " \"pools\": [{ \"pool\": \"192.2.0.1-192.2.0.50\" }]}\n"; + string subnet2 = + " {\"subnet\": \"192.2.1.0/24\", \n" + " \"pools\": [{ \"pool\": \"192.2.1.1-192.2.1.50\" }]}\n"; + string bad_subnet = + " {\"comment\": \"192.2.2.0/24\", \n" + " \"pools\": [{ \"pool\": \"192.2.2.1-192.2.2.50\" }]}\n"; + string subnet_footer = + " ] \n"; + string option_def = + " ,\"option-def\": [\n" + " {\n" + " \"name\": \"foo\",\n" + " \"code\": 163,\n" + " \"type\": \"uint32\",\n" + " \"array\": false,\n" + " \"record-types\": \"\",\n" + " \"space\": \"dhcp4\",\n" + " \"encapsulate\": \"\"\n" + " }\n" + "]\n"; + string option_data = + " ,\"option-data\": [\n" + " {\n" + " \"name\": \"foo\",\n" + " \"code\": 163,\n" + " \"space\": \"dhcp4\",\n" + " \"csv-format\": true,\n" + " \"data\": \"12345\"\n" + " }\n" + "]\n"; + string control_socket_header = + " ,\"control-socket\": { \n" + " \"socket-type\": \"unix\", \n" + " \"socket-name\": \""; + string control_socket_footer = + "\" \n} \n"; + string logger_txt = + " \"Logging\": { \n" + " \"loggers\": [ { \n" + " \"name\": \"kea\", \n" + " \"severity\": \"FATAL\", \n" + " \"output_options\": [{ \n" + " \"output\": \"/dev/null\" \n" + " }] \n" + " }] \n" + " } \n"; - // By default the list should be empty (except the standard list-commands - // supported by the CommandMgr itself) - EXPECT_NO_THROW(answer = CommandMgr::instance().processCommand(list_cmds)); - ASSERT_TRUE(answer); - ASSERT_TRUE(answer->get("arguments")); - EXPECT_EQ("[ \"list-commands\" ]", answer->get("arguments")->str()); + std::ostringstream os; - // Created server should register several additional commands. - ASSERT_NO_THROW( - server_.reset(new NakedControlledDhcpv4Srv()); - ); + // Create a valid config with all the parts should parse + os << set_config_txt << "," + << args_txt + << dhcp4_cfg_txt + << subnet1 + << subnet_footer + << option_def + << option_data + << control_socket_header + << socket_path_ + << control_socket_footer + << "}\n" // close dhcp4 + << "," + << logger_txt + << "}}"; - EXPECT_NO_THROW(answer = CommandMgr::instance().processCommand(list_cmds)); - ASSERT_TRUE(answer); - ASSERT_TRUE(answer->get("arguments")); - std::string command_list = answer->get("arguments")->str(); + // Send the config-set command + std::string response; + sendUnixCommand(os.str(), response); - EXPECT_TRUE(command_list.find("\"list-commands\"") != string::npos); - EXPECT_TRUE(command_list.find("\"build-report\"") != string::npos); - EXPECT_TRUE(command_list.find("\"config-get\"") != string::npos); - EXPECT_TRUE(command_list.find("\"config-set\"") != string::npos); - EXPECT_TRUE(command_list.find("\"config-write\"") != string::npos); - EXPECT_TRUE(command_list.find("\"leases-reclaim\"") != string::npos); - EXPECT_TRUE(command_list.find("\"libreload\"") != string::npos); - EXPECT_TRUE(command_list.find("\"server-tag-get\"") != string::npos); - EXPECT_TRUE(command_list.find("\"shutdown\"") != string::npos); - EXPECT_TRUE(command_list.find("\"statistic-get\"") != string::npos); - EXPECT_TRUE(command_list.find("\"statistic-get-all\"") != string::npos); - EXPECT_TRUE(command_list.find("\"statistic-remove\"") != string::npos); - EXPECT_TRUE(command_list.find("\"statistic-remove-all\"") != string::npos); - EXPECT_TRUE(command_list.find("\"statistic-reset\"") != string::npos); - EXPECT_TRUE(command_list.find("\"statistic-reset-all\"") != string::npos); - EXPECT_TRUE(command_list.find("\"statistic-sample-age-set\"") != string::npos); - EXPECT_TRUE(command_list.find("\"statistic-sample-age-set-all\"") != string::npos); - EXPECT_TRUE(command_list.find("\"statistic-sample-count-set\"") != string::npos); - EXPECT_TRUE(command_list.find("\"statistic-sample-count-set-all\"") != string::npos); - EXPECT_TRUE(command_list.find("\"version-get\"") != string::npos); + // Verify the configuration was successful. + EXPECT_EQ("{ \"result\": 0, \"text\": \"Configuration successful.\" }", + response); - // Ok, and now delete the server. It should deregister its commands. - server_.reset(); + // Check that the config was indeed applied. + const Subnet4Collection* subnets = + CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll(); + EXPECT_EQ(1, subnets->size()); - // The list should be (almost) empty again. - EXPECT_NO_THROW(answer = CommandMgr::instance().processCommand(list_cmds)); - ASSERT_TRUE(answer); - ASSERT_TRUE(answer->get("arguments")); - EXPECT_EQ("[ \"list-commands\" ]", answer->get("arguments")->str()); -} + OptionDefinitionPtr def = LibDHCP::getRuntimeOptionDef("dhcp4", 163); + ASSERT_TRUE(def); -// Tests that the server properly responds to invalid commands sent -// via ControlChannel -TEST_F(CtrlChannelDhcpv4SrvTest, controlChannelNegative) { - createUnixChannelServer(); - std::string response; + // Create a config with malformed subnet that should fail to parse. + os.str(""); + os << set_config_txt << "," + << args_txt + << dhcp4_cfg_txt + << bad_subnet + << subnet_footer + << control_socket_header + << socket_path_ + << control_socket_footer + << "}\n" // close dhcp4 + "}}"; - sendUnixCommand("{ \"command\": \"bogus\" }", response); - EXPECT_EQ("{ \"result\": 2," - " \"text\": \"'bogus' command not supported.\" }", response); + // Send the config-set command + sendUnixCommand(os.str(), response); - sendUnixCommand("utter nonsense", response); + // Should fail with a syntax error EXPECT_EQ("{ \"result\": 1, " - "\"text\": \"invalid first character u\" }", + "\"text\": \"subnet configuration failed: mandatory 'subnet' " + "parameter is missing for a subnet being configured (:19:17)\" }", response); -} - -// Tests that the server properly responds to shtudown command sent -// via ControlChannel -TEST_F(CtrlChannelDhcpv4SrvTest, controlChannelShutdown) { - createUnixChannelServer(); - std::string response; - - sendUnixCommand("{ \"command\": \"shutdown\" }", response); - EXPECT_EQ("{ \"result\": 0, \"text\": \"Shutting down.\" }",response); -} -// This test verifies that the DHCP server immediately reclaims expired -// leases on leases-reclaim command -TEST_F(CtrlChannelDhcpv4SrvTest, controlLeasesReclaim) { - createUnixChannelServer(); + // Check that the config was not lost + subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll(); + EXPECT_EQ(1, subnets->size()); - // Create expired leases. Leases are expired by 40 seconds ago - // (valid lifetime = 60, cltt = now - 100). - HWAddrPtr hwaddr0(new HWAddr(HWAddr::fromText("00:01:02:03:04:05"))); - Lease4Ptr lease0(new Lease4(IOAddress("10.0.0.1"), hwaddr0, - ClientIdPtr(), 60, - time(NULL) - 100, SubnetID(1))); - HWAddrPtr hwaddr1(new HWAddr(HWAddr::fromText("01:02:03:04:05:06"))); - Lease4Ptr lease1(new Lease4(IOAddress("10.0.0.2"), hwaddr1, - ClientIdPtr(), 60, - time(NULL) - 100, SubnetID(1))); + def = LibDHCP::getRuntimeOptionDef("dhcp4", 163); + ASSERT_TRUE(def); - // Add leases to the database. - LeaseMgr& lease_mgr = LeaseMgrFactory::instance(); - ASSERT_NO_THROW(lease_mgr.addLease(lease0)); - ASSERT_NO_THROW(lease_mgr.addLease(lease1)); + // Create a valid config with two subnets and no command channel. + // It should succeed, client should still receive the response + os.str(""); + os << set_config_txt << "," + << args_txt + << dhcp4_cfg_txt + << subnet1 + << ",\n" + << subnet2 + << subnet_footer + << "}\n" // close dhcp4 + << "}}"; - // Make sure they have been added. - ASSERT_TRUE(lease_mgr.getLease4(IOAddress("10.0.0.1"))); - ASSERT_TRUE(lease_mgr.getLease4(IOAddress("10.0.0.2"))); + // Verify the control channel socket exists. + ASSERT_TRUE(fileExists(socket_path_)); - // No arguments - std::string response; - sendUnixCommand("{ \"command\": \"leases-reclaim\" }", response); - EXPECT_EQ("{ \"result\": 1, \"text\": " - "\"Missing mandatory 'remove' parameter.\" }", response); + // Send the config-set command. + sendUnixCommand(os.str(), response); - // Bad argument name - sendUnixCommand("{ \"command\": \"leases-reclaim\", " - "\"arguments\": { \"reclaim\": true } }", response); - EXPECT_EQ("{ \"result\": 1, \"text\": " - "\"Missing mandatory 'remove' parameter.\" }", response); + // Verify the control channel socket no longer exists. + EXPECT_FALSE(fileExists(socket_path_)); - // Bad remove argument type - sendUnixCommand("{ \"command\": \"leases-reclaim\", " - "\"arguments\": { \"remove\": \"bogus\" } }", response); - EXPECT_EQ("{ \"result\": 1, \"text\": " - "\"'remove' parameter expected to be a boolean.\" }", response); + // With no command channel, should still receive the response. + EXPECT_EQ("{ \"result\": 0, \"text\": \"Configuration successful.\" }", + response); - // Send the command - sendUnixCommand("{ \"command\": \"leases-reclaim\", " - "\"arguments\": { \"remove\": false } }", response); - EXPECT_EQ("{ \"result\": 0, \"text\": " - "\"Reclamation of expired leases is complete.\" }", response); + // Check that the config was not lost + subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll(); + EXPECT_EQ(2, subnets->size()); - // Leases should be reclaimed, but not removed - ASSERT_NO_THROW(lease0 = lease_mgr.getLease4(IOAddress("10.0.0.1"))); - ASSERT_NO_THROW(lease1 = lease_mgr.getLease4(IOAddress("10.0.0.2"))); - ASSERT_TRUE(lease0); - ASSERT_TRUE(lease1); - EXPECT_TRUE(lease0->stateExpiredReclaimed()); - EXPECT_TRUE(lease1->stateExpiredReclaimed()); + // Clean up after the test. + CfgMgr::instance().clear(); } -// This test verifies that the DHCP server handles version-get commands -TEST_F(CtrlChannelDhcpv4SrvTest, getversion) { - createUnixChannelServer(); - - std::string response; - - // Send the version-get command - sendUnixCommand("{ \"command\": \"version-get\" }", response); - EXPECT_TRUE(response.find("\"result\": 0") != string::npos); - EXPECT_TRUE(response.find("log4cplus") != string::npos); - EXPECT_FALSE(response.find("GTEST_VERSION") != string::npos); - - // Send the build-report command - sendUnixCommand("{ \"command\": \"build-report\" }", response); - EXPECT_TRUE(response.find("\"result\": 0") != string::npos); - EXPECT_TRUE(response.find("GTEST_VERSION") != string::npos); -} - -// This test verifies that the DHCP server handles server-tag-get command -TEST_F(CtrlChannelDhcpv4SrvTest, serverTagGet) { - createUnixChannelServer(); - - std::string response; - std::string expected; - - // Send the server-tag-get command - sendUnixCommand("{ \"command\": \"server-tag-get\" }", response); - expected = "{ \"arguments\": { \"server-tag\": \"\" }, \"result\": 0 }"; - EXPECT_EQ(expected, response); - - // Set a value to the server tag - CfgMgr::instance().getCurrentCfg()->setServerTag("foobar"); - - // Retry... - sendUnixCommand("{ \"command\": \"server-tag-get\" }", response); - expected = "{ \"arguments\": { \"server-tag\": \"foobar\" }, \"result\": 0 }"; -} - -// This test verifies that the DHCP server immediately removed expired -// This test verifies that the DHCP server immediately removed expired -// leases on leases-reclaim command with remove = true -TEST_F(CtrlChannelDhcpv4SrvTest, controlLeasesReclaimRemove) { - createUnixChannelServer(); - - // Create expired leases. Leases are expired by 40 seconds ago - // (valid lifetime = 60, cltt = now - 100). - HWAddrPtr hwaddr0(new HWAddr(HWAddr::fromText("00:01:02:03:04:05"))); - Lease4Ptr lease0(new Lease4(IOAddress("10.0.0.1"), hwaddr0, - ClientIdPtr(), 60, - time(NULL) - 100, SubnetID(1))); - HWAddrPtr hwaddr1(new HWAddr(HWAddr::fromText("01:02:03:04:05:06"))); - Lease4Ptr lease1(new Lease4(IOAddress("10.0.0.2"), hwaddr1, - ClientIdPtr(), 60, - time(NULL) - 100, SubnetID(1))); - - // Add leases to the database. - LeaseMgr& lease_mgr = LeaseMgrFactory::instance(); - ASSERT_NO_THROW(lease_mgr.addLease(lease0)); - ASSERT_NO_THROW(lease_mgr.addLease(lease1)); - - // Make sure they have been added. - ASSERT_TRUE(lease_mgr.getLease4(IOAddress("10.0.0.1"))); - ASSERT_TRUE(lease_mgr.getLease4(IOAddress("10.0.0.2"))); - - // Send the command - std::string response; - sendUnixCommand("{ \"command\": \"leases-reclaim\", " - "\"arguments\": { \"remove\": true } }", response); - EXPECT_EQ("{ \"result\": 0, \"text\": " - "\"Reclamation of expired leases is complete.\" }", response); - - // Leases should have been removed. - ASSERT_NO_THROW(lease0 = lease_mgr.getLease4(IOAddress("10.0.0.1"))); - ASSERT_NO_THROW(lease1 = lease_mgr.getLease4(IOAddress("10.0.0.2"))); - EXPECT_FALSE(lease0); - EXPECT_FALSE(lease1); -} - -// Tests that the server properly responds to statistics commands. Note this -// is really only intended to verify that the appropriate Statistics handler -// is called based on the command. It is not intended to be an exhaustive -// test of Dhcpv4 statistics. -TEST_F(CtrlChannelDhcpv4SrvTest, controlChannelStats) { - createUnixChannelServer(); - std::string response; - - // Check statistic-get - sendUnixCommand("{ \"command\" : \"statistic-get\", " - " \"arguments\": {" - " \"name\":\"bogus\" }}", response); - EXPECT_EQ("{ \"arguments\": { }, \"result\": 0 }", response); - - // Check statistic-get-all - sendUnixCommand("{ \"command\" : \"statistic-get-all\", " - " \"arguments\": {}}", response); - EXPECT_EQ("{ \"arguments\": { }, \"result\": 0 }", response); - - // Check statistic-reset - sendUnixCommand("{ \"command\" : \"statistic-reset\", " - " \"arguments\": {" - " \"name\":\"bogus\" }}", response); - EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }", - response); - - // Check statistic-reset-all - sendUnixCommand("{ \"command\" : \"statistic-reset-all\", " - " \"arguments\": {}}", response); - EXPECT_EQ("{ \"result\": 0, \"text\": " - "\"All statistics reset to neutral values.\" }", response); - - // Check statistic-remove - sendUnixCommand("{ \"command\" : \"statistic-remove\", " - " \"arguments\": {" - " \"name\":\"bogus\" }}", response); - EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }", - response); - - // Check statistic-remove-all - sendUnixCommand("{ \"command\" : \"statistic-remove-all\", " - " \"arguments\": {}}", response); - EXPECT_EQ("{ \"result\": 0, \"text\": \"All statistics removed.\" }", - response); - - // Check statistic-sample-age-set - sendUnixCommand("{ \"command\" : \"statistic-sample-age-set\", " - " \"arguments\": {" - " \"name\":\"bogus\", \"duration\": 1245 }}", response); - EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }", - response); - - // Check statistic-sample-age-set-all - sendUnixCommand("{ \"command\" : \"statistic-sample-age-set-all\", " - " \"arguments\": {" - " \"duration\": 1245 }}", response); - EXPECT_EQ("{ \"result\": 0, \"text\": \"All statistics duration limit are set.\" }", - response); - - // Check statistic-sample-count-set - sendUnixCommand("{ \"command\" : \"statistic-sample-count-set\", " - " \"arguments\": {" - " \"name\":\"bogus\", \"max-samples\": 100 }}", response); - EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }", - response); - - // Check statistic-sample-count-set-all - sendUnixCommand("{ \"command\" : \"statistic-sample-count-set-all\", " - " \"arguments\": {" - " \"max-samples\": 100 }}", response); - EXPECT_EQ("{ \"result\": 0, \"text\": \"All statistics count limit are set.\" }", - response); -} - -// Check that the "config-set" command will replace current configuration -TEST_F(CtrlChannelDhcpv4SrvTest, configSet) { +// Verify that the "config-test" command will do what we expect. +TEST_F(CtrlChannelDhcpv4SrvTest, configTest) { createUnixChannelServer(); // Define strings to permutate the config arguments // (Note the line feeds makes errors easy to find) string set_config_txt = "{ \"command\": \"config-set\" \n"; + string config_test_txt = "{ \"command\": \"config-test\" \n"; string args_txt = " \"arguments\": { \n"; string dhcp4_cfg_txt = " \"Dhcp4\": { \n" @@ -829,7 +713,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, configSet) { // Create a config with malformed subnet that should fail to parse. os.str(""); - os << set_config_txt << "," + os << config_test_txt << "," << args_txt << dhcp4_cfg_txt << bad_subnet @@ -840,13 +724,13 @@ TEST_F(CtrlChannelDhcpv4SrvTest, configSet) { << "}\n" // close dhcp4 "}}"; - // Send the config-set command + // Send the config-test command sendUnixCommand(os.str(), response); // Should fail with a syntax error EXPECT_EQ("{ \"result\": 1, " - "\"text\": \"subnet configuration failed: mandatory 'subnet' " - "parameter is missing for a subnet being configured (:19:17)\" }", + "\"text\": \"subnet configuration failed: mandatory 'subnet' parameter " + "is missing for a subnet being configured (:19:17)\" }", response); // Check that the config was not lost @@ -854,9 +738,8 @@ TEST_F(CtrlChannelDhcpv4SrvTest, configSet) { EXPECT_EQ(1, subnets->size()); // Create a valid config with two subnets and no command channel. - // It should succeed, client should still receive the response os.str(""); - os << set_config_txt << "," + os << config_test_txt << "," << args_txt << dhcp4_cfg_txt << subnet1 @@ -869,231 +752,357 @@ TEST_F(CtrlChannelDhcpv4SrvTest, configSet) { // Verify the control channel socket exists. ASSERT_TRUE(fileExists(socket_path_)); - // Send the config-set command. + // Send the config-test command. sendUnixCommand(os.str(), response); - // Verify the control channel socket no longer exists. - EXPECT_FALSE(fileExists(socket_path_)); + // Verify the control channel socket still exists. + EXPECT_TRUE(fileExists(socket_path_)); - // With no command channel, should still receive the response. - EXPECT_EQ("{ \"result\": 0, \"text\": \"Configuration successful.\" }", + // Verify the configuration was successful. + EXPECT_EQ("{ \"result\": 0, \"text\": \"Configuration seems sane. " + "Control-socket, hook-libraries, and D2 configuration were " + "sanity checked, but not applied.\" }", response); - // Check that the config was not lost + // Check that the config was not applied. subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll(); - EXPECT_EQ(2, subnets->size()); + EXPECT_EQ(1, subnets->size()); // Clean up after the test. CfgMgr::instance().clear(); } +// This test checks which commands are registered by the DHCPv4 server. +TEST_F(CtrlChannelDhcpv4SrvTest, commandsRegistration) { -// Tests that the server properly responds to shtudown command sent -// via ControlChannel -TEST_F(CtrlChannelDhcpv4SrvTest, listCommands) { - createUnixChannelServer(); - std::string response; - - sendUnixCommand("{ \"command\": \"list-commands\" }", response); + ConstElementPtr list_cmds = createCommand("list-commands"); + ConstElementPtr answer; - ConstElementPtr rsp; - EXPECT_NO_THROW(rsp = Element::fromJSON(response)); + // By default the list should be empty (except the standard list-commands + // supported by the CommandMgr itself) + EXPECT_NO_THROW(answer = CommandMgr::instance().processCommand(list_cmds)); + ASSERT_TRUE(answer); + ASSERT_TRUE(answer->get("arguments")); + EXPECT_EQ("[ \"list-commands\" ]", answer->get("arguments")->str()); - // We expect the server to report at least the following commands: - checkListCommands(rsp, "build-report"); - checkListCommands(rsp, "config-get"); - checkListCommands(rsp, "config-reload"); - checkListCommands(rsp, "config-set"); - checkListCommands(rsp, "config-write"); - checkListCommands(rsp, "list-commands"); - checkListCommands(rsp, "leases-reclaim"); - checkListCommands(rsp, "libreload"); - checkListCommands(rsp, "server-tag-get"); - checkListCommands(rsp, "shutdown"); - checkListCommands(rsp, "statistic-get"); - checkListCommands(rsp, "statistic-get-all"); - checkListCommands(rsp, "statistic-remove"); - checkListCommands(rsp, "statistic-remove-all"); - checkListCommands(rsp, "statistic-reset"); - checkListCommands(rsp, "statistic-reset-all"); - checkListCommands(rsp, "statistic-sample-age-set"); - checkListCommands(rsp, "statistic-sample-age-set-all"); - checkListCommands(rsp, "statistic-sample-count-set"); - checkListCommands(rsp, "statistic-sample-count-set-all"); - checkListCommands(rsp, "version-get"); -} + // Created server should register several additional commands. + ASSERT_NO_THROW( + server_.reset(new NakedControlledDhcpv4Srv()); + ); -// Tests if the server returns its configuration using config-get. -// Note there are separate tests that verify if toElement() called by the -// config-get handler are actually converting the configuration correctly. -TEST_F(CtrlChannelDhcpv4SrvTest, configGet) { - createUnixChannelServer(); - std::string response; + EXPECT_NO_THROW(answer = CommandMgr::instance().processCommand(list_cmds)); + ASSERT_TRUE(answer); - sendUnixCommand("{ \"command\": \"config-get\" }", response); - ConstElementPtr rsp; + ASSERT_TRUE(answer->get("arguments")); + std::string command_list = answer->get("arguments")->str(); - // The response should be a valid JSON. - EXPECT_NO_THROW(rsp = Element::fromJSON(response)); - ASSERT_TRUE(rsp); + EXPECT_TRUE(command_list.find("\"list-commands\"") != string::npos); + EXPECT_TRUE(command_list.find("\"build-report\"") != string::npos); + EXPECT_TRUE(command_list.find("\"config-get\"") != string::npos); + EXPECT_TRUE(command_list.find("\"config-set\"") != string::npos); + EXPECT_TRUE(command_list.find("\"config-write\"") != string::npos); + EXPECT_TRUE(command_list.find("\"leases-reclaim\"") != string::npos); + EXPECT_TRUE(command_list.find("\"libreload\"") != string::npos); + EXPECT_TRUE(command_list.find("\"server-tag-get\"") != string::npos); + EXPECT_TRUE(command_list.find("\"shutdown\"") != string::npos); + EXPECT_TRUE(command_list.find("\"statistic-get\"") != string::npos); + EXPECT_TRUE(command_list.find("\"statistic-get-all\"") != string::npos); + EXPECT_TRUE(command_list.find("\"statistic-remove\"") != string::npos); + EXPECT_TRUE(command_list.find("\"statistic-remove-all\"") != string::npos); + EXPECT_TRUE(command_list.find("\"statistic-reset\"") != string::npos); + EXPECT_TRUE(command_list.find("\"statistic-reset-all\"") != string::npos); + EXPECT_TRUE(command_list.find("\"statistic-sample-age-set\"") != string::npos); + EXPECT_TRUE(command_list.find("\"statistic-sample-age-set-all\"") != string::npos); + EXPECT_TRUE(command_list.find("\"statistic-sample-count-set\"") != string::npos); + EXPECT_TRUE(command_list.find("\"statistic-sample-count-set-all\"") != string::npos); + EXPECT_TRUE(command_list.find("\"version-get\"") != string::npos); - int status; - ConstElementPtr cfg = parseAnswer(status, rsp); - EXPECT_EQ(CONTROL_RESULT_SUCCESS, status); + // Ok, and now delete the server. It should deregister its commands. + server_.reset(); - // Ok, now roughly check if the response seems legit. - ASSERT_TRUE(cfg); - ASSERT_EQ(Element::map, cfg->getType()); - EXPECT_TRUE(cfg->get("Dhcp4")); + // The list should be (almost) empty again. + EXPECT_NO_THROW(answer = CommandMgr::instance().processCommand(list_cmds)); + ASSERT_TRUE(answer); + ASSERT_TRUE(answer->get("arguments")); + EXPECT_EQ("[ \"list-commands\" ]", answer->get("arguments")->str()); } -// Verify that the "config-test" command will do what we expect. -TEST_F(CtrlChannelDhcpv4SrvTest, configTest) { +// Tests that the server properly responds to invalid commands sent +// via ControlChannel +TEST_F(CtrlChannelDhcpv4SrvTest, controlChannelNegative) { createUnixChannelServer(); + std::string response; - // Define strings to permutate the config arguments - // (Note the line feeds makes errors easy to find) - string set_config_txt = "{ \"command\": \"config-set\" \n"; - string config_test_txt = "{ \"command\": \"config-test\" \n"; - string args_txt = " \"arguments\": { \n"; - string dhcp4_cfg_txt = - " \"Dhcp4\": { \n" - " \"interfaces-config\": { \n" - " \"interfaces\": [\"*\"] \n" - " }, \n" - " \"valid-lifetime\": 4000, \n" - " \"renew-timer\": 1000, \n" - " \"rebind-timer\": 2000, \n" - " \"lease-database\": { \n" - " \"type\": \"memfile\", \n" - " \"persist\":false, \n" - " \"lfc-interval\": 0 \n" - " }, \n" - " \"expired-leases-processing\": { \n" - " \"reclaim-timer-wait-time\": 0, \n" - " \"hold-reclaimed-time\": 0, \n" - " \"flush-reclaimed-timer-wait-time\": 0 \n" - " }," - " \"subnet4\": [ \n"; - string subnet1 = - " {\"subnet\": \"192.2.0.0/24\", \n" - " \"pools\": [{ \"pool\": \"192.2.0.1-192.2.0.50\" }]}\n"; - string subnet2 = - " {\"subnet\": \"192.2.1.0/24\", \n" - " \"pools\": [{ \"pool\": \"192.2.1.1-192.2.1.50\" }]}\n"; - string bad_subnet = - " {\"comment\": \"192.2.2.0/24\", \n" - " \"pools\": [{ \"pool\": \"192.2.2.1-192.2.2.50\" }]}\n"; - string subnet_footer = - " ] \n"; - string control_socket_header = - " ,\"control-socket\": { \n" - " \"socket-type\": \"unix\", \n" - " \"socket-name\": \""; - string control_socket_footer = - "\" \n} \n"; - string logger_txt = - " \"Logging\": { \n" - " \"loggers\": [ { \n" - " \"name\": \"kea\", \n" - " \"severity\": \"FATAL\", \n" - " \"output_options\": [{ \n" - " \"output\": \"/dev/null\" \n" - " }] \n" - " }] \n" - " } \n"; + sendUnixCommand("{ \"command\": \"bogus\" }", response); + EXPECT_EQ("{ \"result\": 2," + " \"text\": \"'bogus' command not supported.\" }", response); - std::ostringstream os; + sendUnixCommand("utter nonsense", response); + EXPECT_EQ("{ \"result\": 1, " + "\"text\": \"invalid first character u\" }", + response); +} - // Create a valid config with all the parts should parse - os << set_config_txt << "," - << args_txt - << dhcp4_cfg_txt - << subnet1 - << subnet_footer - << control_socket_header - << socket_path_ - << control_socket_footer - << "}\n" // close dhcp4 - << "," - << logger_txt - << "}}"; +// Tests that the server properly responds to shtudown command sent +// via ControlChannel +TEST_F(CtrlChannelDhcpv4SrvTest, controlChannelShutdown) { + createUnixChannelServer(); + std::string response; + + sendUnixCommand("{ \"command\": \"shutdown\" }", response); + EXPECT_EQ("{ \"result\": 0, \"text\": \"Shutting down.\" }",response); +} + +// This test verifies that the DHCP server handles version-get commands +TEST_F(CtrlChannelDhcpv4SrvTest, getversion) { + createUnixChannelServer(); - // Send the config-set command std::string response; - sendUnixCommand(os.str(), response); - // Verify the configuration was successful. - EXPECT_EQ("{ \"result\": 0, \"text\": \"Configuration successful.\" }", - response); + // Send the version-get command + sendUnixCommand("{ \"command\": \"version-get\" }", response); + EXPECT_TRUE(response.find("\"result\": 0") != string::npos); + EXPECT_TRUE(response.find("log4cplus") != string::npos); + EXPECT_FALSE(response.find("GTEST_VERSION") != string::npos); - // Check that the config was indeed applied. - const Subnet4Collection* subnets = - CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll(); - EXPECT_EQ(1, subnets->size()); + // Send the build-report command + sendUnixCommand("{ \"command\": \"build-report\" }", response); + EXPECT_TRUE(response.find("\"result\": 0") != string::npos); + EXPECT_TRUE(response.find("GTEST_VERSION") != string::npos); +} - // Create a config with malformed subnet that should fail to parse. - os.str(""); - os << config_test_txt << "," - << args_txt - << dhcp4_cfg_txt - << bad_subnet - << subnet_footer - << control_socket_header - << socket_path_ - << control_socket_footer - << "}\n" // close dhcp4 - "}}"; +// This test verifies that the DHCP server immediately reclaims expired +// leases on leases-reclaim command +TEST_F(CtrlChannelDhcpv4SrvTest, controlLeasesReclaim) { + createUnixChannelServer(); - // Send the config-test command - sendUnixCommand(os.str(), response); + // Create expired leases. Leases are expired by 40 seconds ago + // (valid lifetime = 60, cltt = now - 100). + HWAddrPtr hwaddr0(new HWAddr(HWAddr::fromText("00:01:02:03:04:05"))); + Lease4Ptr lease0(new Lease4(IOAddress("10.0.0.1"), hwaddr0, + ClientIdPtr(), 60, + time(NULL) - 100, SubnetID(1))); + HWAddrPtr hwaddr1(new HWAddr(HWAddr::fromText("01:02:03:04:05:06"))); + Lease4Ptr lease1(new Lease4(IOAddress("10.0.0.2"), hwaddr1, + ClientIdPtr(), 60, + time(NULL) - 100, SubnetID(1))); - // Should fail with a syntax error - EXPECT_EQ("{ \"result\": 1, " - "\"text\": \"subnet configuration failed: mandatory 'subnet' " - "parameter is missing for a subnet being configured (:19:17)\" }", + // Add leases to the database. + LeaseMgr& lease_mgr = LeaseMgrFactory::instance(); + ASSERT_NO_THROW(lease_mgr.addLease(lease0)); + ASSERT_NO_THROW(lease_mgr.addLease(lease1)); + + // Make sure they have been added. + ASSERT_TRUE(lease_mgr.getLease4(IOAddress("10.0.0.1"))); + ASSERT_TRUE(lease_mgr.getLease4(IOAddress("10.0.0.2"))); + + // No arguments + std::string response; + sendUnixCommand("{ \"command\": \"leases-reclaim\" }", response); + EXPECT_EQ("{ \"result\": 1, \"text\": " + "\"Missing mandatory 'remove' parameter.\" }", response); + + // Bad argument name + sendUnixCommand("{ \"command\": \"leases-reclaim\", " + "\"arguments\": { \"reclaim\": true } }", response); + EXPECT_EQ("{ \"result\": 1, \"text\": " + "\"Missing mandatory 'remove' parameter.\" }", response); + + // Bad remove argument type + sendUnixCommand("{ \"command\": \"leases-reclaim\", " + "\"arguments\": { \"remove\": \"bogus\" } }", response); + EXPECT_EQ("{ \"result\": 1, \"text\": " + "\"'remove' parameter expected to be a boolean.\" }", response); + + // Send the command + sendUnixCommand("{ \"command\": \"leases-reclaim\", " + "\"arguments\": { \"remove\": false } }", response); + EXPECT_EQ("{ \"result\": 0, \"text\": " + "\"Reclamation of expired leases is complete.\" }", response); + + // Leases should be reclaimed, but not removed + ASSERT_NO_THROW(lease0 = lease_mgr.getLease4(IOAddress("10.0.0.1"))); + ASSERT_NO_THROW(lease1 = lease_mgr.getLease4(IOAddress("10.0.0.2"))); + ASSERT_TRUE(lease0); + ASSERT_TRUE(lease1); + EXPECT_TRUE(lease0->stateExpiredReclaimed()); + EXPECT_TRUE(lease1->stateExpiredReclaimed()); +} + +// This test verifies that the DHCP server immediately reclaims expired +// leases on leases-reclaim command with remove = true +TEST_F(CtrlChannelDhcpv4SrvTest, controlLeasesReclaimRemove) { + createUnixChannelServer(); + + // Create expired leases. Leases are expired by 40 seconds ago + // (valid lifetime = 60, cltt = now - 100). + HWAddrPtr hwaddr0(new HWAddr(HWAddr::fromText("00:01:02:03:04:05"))); + Lease4Ptr lease0(new Lease4(IOAddress("10.0.0.1"), hwaddr0, + ClientIdPtr(), 60, + time(NULL) - 100, SubnetID(1))); + HWAddrPtr hwaddr1(new HWAddr(HWAddr::fromText("01:02:03:04:05:06"))); + Lease4Ptr lease1(new Lease4(IOAddress("10.0.0.2"), hwaddr1, + ClientIdPtr(), 60, + time(NULL) - 100, SubnetID(1))); + + // Add leases to the database. + LeaseMgr& lease_mgr = LeaseMgrFactory::instance(); + ASSERT_NO_THROW(lease_mgr.addLease(lease0)); + ASSERT_NO_THROW(lease_mgr.addLease(lease1)); + + // Make sure they have been added. + ASSERT_TRUE(lease_mgr.getLease4(IOAddress("10.0.0.1"))); + ASSERT_TRUE(lease_mgr.getLease4(IOAddress("10.0.0.2"))); + + // Send the command + std::string response; + sendUnixCommand("{ \"command\": \"leases-reclaim\", " + "\"arguments\": { \"remove\": true } }", response); + EXPECT_EQ("{ \"result\": 0, \"text\": " + "\"Reclamation of expired leases is complete.\" }", response); + + // Leases should have been removed. + ASSERT_NO_THROW(lease0 = lease_mgr.getLease4(IOAddress("10.0.0.1"))); + ASSERT_NO_THROW(lease1 = lease_mgr.getLease4(IOAddress("10.0.0.2"))); + EXPECT_FALSE(lease0); + EXPECT_FALSE(lease1); +} + +// Tests that the server properly responds to statistics commands. Note this +// is really only intended to verify that the appropriate Statistics handler +// is called based on the command. It is not intended to be an exhaustive +// test of Dhcpv4 statistics. +TEST_F(CtrlChannelDhcpv4SrvTest, controlChannelStats) { + createUnixChannelServer(); + std::string response; + + // Check statistic-get + sendUnixCommand("{ \"command\" : \"statistic-get\", " + " \"arguments\": {" + " \"name\":\"bogus\" }}", response); + EXPECT_EQ("{ \"arguments\": { }, \"result\": 0 }", response); + + // Check statistic-get-all + sendUnixCommand("{ \"command\" : \"statistic-get-all\", " + " \"arguments\": {}}", response); + EXPECT_EQ("{ \"arguments\": { }, \"result\": 0 }", response); + + // Check statistic-reset + sendUnixCommand("{ \"command\" : \"statistic-reset\", " + " \"arguments\": {" + " \"name\":\"bogus\" }}", response); + EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }", response); - // Check that the config was not lost - subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll(); - EXPECT_EQ(1, subnets->size()); + // Check statistic-reset-all + sendUnixCommand("{ \"command\" : \"statistic-reset-all\", " + " \"arguments\": {}}", response); + EXPECT_EQ("{ \"result\": 0, \"text\": " + "\"All statistics reset to neutral values.\" }", response); - // Create a valid config with two subnets and no command channel. - os.str(""); - os << config_test_txt << "," - << args_txt - << dhcp4_cfg_txt - << subnet1 - << ",\n" - << subnet2 - << subnet_footer - << "}\n" // close dhcp4 - << "}}"; + // Check statistic-remove + sendUnixCommand("{ \"command\" : \"statistic-remove\", " + " \"arguments\": {" + " \"name\":\"bogus\" }}", response); + EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }", + response); - // Verify the control channel socket exists. - ASSERT_TRUE(fileExists(socket_path_)); + // Check statistic-remove-all + sendUnixCommand("{ \"command\" : \"statistic-remove-all\", " + " \"arguments\": {}}", response); + EXPECT_EQ("{ \"result\": 0, \"text\": \"All statistics removed.\" }", + response); - // Send the config-test command - sendUnixCommand(os.str(), response); + // Check statistic-sample-age-set + sendUnixCommand("{ \"command\" : \"statistic-sample-age-set\", " + " \"arguments\": {" + " \"name\":\"bogus\", \"duration\": 1245 }}", response); + EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }", + response); - // Verify the control channel socket still exists. - EXPECT_TRUE(fileExists(socket_path_)); + // Check statistic-sample-age-set-all + sendUnixCommand("{ \"command\" : \"statistic-sample-age-set-all\", " + " \"arguments\": {" + " \"duration\": 1245 }}", response); + EXPECT_EQ("{ \"result\": 0, \"text\": \"All statistics duration limit are set.\" }", + response); - // Verify the configuration was successful. - EXPECT_EQ("{ \"result\": 0, \"text\": \"Configuration seems sane. " - "Control-socket, hook-libraries, and D2 configuration were " - "sanity checked, but not applied.\" }", + // Check statistic-sample-count-set + sendUnixCommand("{ \"command\" : \"statistic-sample-count-set\", " + " \"arguments\": {" + " \"name\":\"bogus\", \"max-samples\": 100 }}", response); + EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }", response); - // Check that the config was not applied - subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll(); - EXPECT_EQ(1, subnets->size()); + // Check statistic-sample-count-set-all + sendUnixCommand("{ \"command\" : \"statistic-sample-count-set-all\", " + " \"arguments\": {" + " \"max-samples\": 100 }}", response); + EXPECT_EQ("{ \"result\": 0, \"text\": \"All statistics count limit are set.\" }", + response); +} - // Clean up after the test. - CfgMgr::instance().clear(); +// Tests that the server properly responds to shtudown command sent +// via ControlChannel +TEST_F(CtrlChannelDhcpv4SrvTest, listCommands) { + createUnixChannelServer(); + std::string response; + + sendUnixCommand("{ \"command\": \"list-commands\" }", response); + + ConstElementPtr rsp; + EXPECT_NO_THROW(rsp = Element::fromJSON(response)); + + // We expect the server to report at least the following commands: + checkListCommands(rsp, "build-report"); + checkListCommands(rsp, "config-get"); + checkListCommands(rsp, "config-reload"); + checkListCommands(rsp, "config-set"); + checkListCommands(rsp, "config-test"); + checkListCommands(rsp, "config-write"); + checkListCommands(rsp, "list-commands"); + checkListCommands(rsp, "leases-reclaim"); + checkListCommands(rsp, "libreload"); + checkListCommands(rsp, "version-get"); + checkListCommands(rsp, "server-tag-get"); + checkListCommands(rsp, "shutdown"); + checkListCommands(rsp, "statistic-get"); + checkListCommands(rsp, "statistic-get-all"); + checkListCommands(rsp, "statistic-remove"); + checkListCommands(rsp, "statistic-remove-all"); + checkListCommands(rsp, "statistic-reset"); + checkListCommands(rsp, "statistic-reset-all"); + checkListCommands(rsp, "statistic-sample-age-set"); + checkListCommands(rsp, "statistic-sample-age-set-all"); + checkListCommands(rsp, "statistic-sample-count-set"); + checkListCommands(rsp, "statistic-sample-count-set-all"); +} + +// Tests if the server returns its configuration using config-get. +// Note there are separate tests that verify if toElement() called by the +// config-get handler are actually converting the configuration correctly. +TEST_F(CtrlChannelDhcpv4SrvTest, configGet) { + createUnixChannelServer(); + std::string response; + + sendUnixCommand("{ \"command\": \"config-get\" }", response); + ConstElementPtr rsp; + + // The response should be a valid JSON. + EXPECT_NO_THROW(rsp = Element::fromJSON(response)); + ASSERT_TRUE(rsp); + + int status; + ConstElementPtr cfg = parseAnswer(status, rsp); + EXPECT_EQ(CONTROL_RESULT_SUCCESS, status); + + // Ok, now roughly check if the response seems legit. + ASSERT_TRUE(cfg); + ASSERT_EQ(Element::map, cfg->getType()); + EXPECT_TRUE(cfg->get("Dhcp4")); } // Tests if config-write can be called without any parameters. -TEST_F(CtrlChannelDhcpv4SrvTest, writeConfigNoFilename) { +TEST_F(CtrlChannelDhcpv4SrvTest, configWriteNoFilename) { createUnixChannelServer(); std::string response; @@ -1109,7 +1118,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, writeConfigNoFilename) { } // Tests if config-write can be called with a valid filename as parameter. -TEST_F(CtrlChannelDhcpv4SrvTest, writeConfigFilename) { +TEST_F(CtrlChannelDhcpv4SrvTest, configWriteFilename) { createUnixChannelServer(); std::string response; @@ -1472,7 +1481,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, longResponse) { } // This test verifies that the server signals timeout if the transmission -// takes too long, after receiving a partial command. +// takes too long, having received a partial command. TEST_F(CtrlChannelDhcpv4SrvTest, connectionTimeoutPartialCommand) { createUnixChannelServer(); @@ -1522,7 +1531,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, connectionTimeoutPartialCommand) { // Check that the server has signalled a timeout. EXPECT_EQ("{ \"result\": 1, \"text\": " "\"Connection over control channel timed out, " - "discarded partial command of 19 bytes\" }" , response); + "discarded partial command of 19 bytes\" }", response); } // This test verifies that the server signals timeout if the transmission @@ -1552,6 +1561,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, connectionTimeoutNoData) { ASSERT_TRUE(client); ASSERT_TRUE(client->connectToServer(socket_path_)); + // Having sent nothing let's just wait and see if Server times us out. // Let's wait up to 15s for the server's response. The response // should arrive sooner assuming that the timeout mechanism for // the server is working properly. diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc index 9e5947606c..f113106a2a 100644 --- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc +++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc @@ -12,10 +12,11 @@ #include #include #include -#include #include +#include #include #include +#include #include #include #include @@ -24,8 +25,8 @@ using namespace isc::config; using namespace isc::db; -using namespace isc::dhcp; using namespace isc::data; +using namespace isc::dhcp; using namespace isc::hooks; using namespace isc::stats; using namespace std; diff --git a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc index ee821e37a4..245c244bdc 100644 --- a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -17,8 +18,8 @@ #include #include #include -#include #include +#include #include #include "marker_file.h" @@ -38,6 +39,7 @@ #include using namespace std; +using namespace isc; using namespace isc::asiolink; using namespace isc::config; using namespace isc::data; @@ -219,6 +221,8 @@ public: /// @brief Reset void reset() { CtrlDhcpv6SrvTest::reset(); + + // Remove unix socket file static_cast(::remove(socket_path_.c_str())); } @@ -392,7 +396,6 @@ public: } }; - TEST_F(CtrlDhcpv6SrvTest, commands) { boost::scoped_ptr srv; @@ -406,12 +409,12 @@ TEST_F(CtrlDhcpv6SrvTest, commands) { // Case 1: send bogus command ConstElementPtr result = ControlledDhcpv6Srv::processCommand("blah", params); - ConstElementPtr comment = isc::config::parseAnswer(rcode, result); + ConstElementPtr comment = parseAnswer(rcode, result); EXPECT_EQ(1, rcode); // expect failure (no such command as blah) // Case 2: send shutdown command without any parameters result = ControlledDhcpv6Srv::processCommand("shutdown", params); - comment = isc::config::parseAnswer(rcode, result); + comment = parseAnswer(rcode, result); EXPECT_EQ(0, rcode); // expect success const pid_t pid(getpid()); @@ -420,8 +423,8 @@ TEST_F(CtrlDhcpv6SrvTest, commands) { // Case 3: send shutdown command with 1 parameter: pid result = ControlledDhcpv6Srv::processCommand("shutdown", params); - comment = isc::config::parseAnswer(rcode, result); - EXPECT_EQ(0, rcode); // Expect success + comment = parseAnswer(rcode, result); + EXPECT_EQ(0, rcode); // expect success } // Check that the "libreload" command will reload libraries @@ -504,6 +507,28 @@ TEST_F(CtrlChannelDhcpv6SrvTest, configSet) { " \"pools\": [{ \"pool\": \"3005::100-3005::200\" }]}\n"; string subnet_footer = " ] \n"; + string option_def = + " ,\"option-def\": [\n" + " {\n" + " \"name\": \"foo\",\n" + " \"code\": 163,\n" + " \"type\": \"uint32\",\n" + " \"array\": false,\n" + " \"record-types\": \"\",\n" + " \"space\": \"dhcp6\",\n" + " \"encapsulate\": \"\"\n" + " }\n" + "]\n"; + string option_data = + " ,\"option-data\": [\n" + " {\n" + " \"name\": \"foo\",\n" + " \"code\": 163,\n" + " \"space\": \"dhcp6\",\n" + " \"csv-format\": true,\n" + " \"data\": \"12345\"\n" + " }\n" + "]\n"; string control_socket_header = " ,\"control-socket\": { \n" " \"socket-type\": \"unix\", \n" @@ -529,6 +554,8 @@ TEST_F(CtrlChannelDhcpv6SrvTest, configSet) { << dhcp6_cfg_txt << subnet1 << subnet_footer + << option_def + << option_data << control_socket_header << socket_path_ << control_socket_footer @@ -550,6 +577,9 @@ TEST_F(CtrlChannelDhcpv6SrvTest, configSet) { CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll(); EXPECT_EQ(1, subnets->size()); + OptionDefinitionPtr def = LibDHCP::getRuntimeOptionDef("dhcp6", 163); + ASSERT_TRUE(def); + // Create a config with malformed subnet that should fail to parse. os.str(""); os << set_config_txt << "," @@ -568,15 +598,19 @@ TEST_F(CtrlChannelDhcpv6SrvTest, configSet) { // Should fail with a syntax error EXPECT_EQ("{ \"result\": 1, " - "\"text\": \"subnet configuration failed: mandatory 'subnet' parameter is missing for a subnet being configured (:20:17)\" }", + "\"text\": \"subnet configuration failed: mandatory 'subnet' " + "parameter is missing for a subnet being configured (:20:17)\" }", response); // Check that the config was not lost subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll(); EXPECT_EQ(1, subnets->size()); + def = LibDHCP::getRuntimeOptionDef("dhcp6", 163); + ASSERT_TRUE(def); + // Create a valid config with two subnets and no command channel. - // It should succeed but client will not receive a the response + // It should succeed, client should still receive the response os.str(""); os << set_config_txt << "," << args_txt @@ -609,7 +643,7 @@ TEST_F(CtrlChannelDhcpv6SrvTest, configSet) { CfgMgr::instance().clear(); } - // Verify that the "config-test" command will do what we expect. +// Verify that the "config-test" command will do what we expect. TEST_F(CtrlChannelDhcpv6SrvTest, configTest) { createUnixChannelServer(); @@ -786,10 +820,10 @@ TEST_F(CtrlDhcpv6SrvTest, commandsRegistration) { EXPECT_TRUE(command_list.find("\"list-commands\"") != string::npos); EXPECT_TRUE(command_list.find("\"build-report\"") != string::npos); EXPECT_TRUE(command_list.find("\"config-get\"") != string::npos); + EXPECT_TRUE(command_list.find("\"config-set\"") != string::npos); EXPECT_TRUE(command_list.find("\"config-write\"") != string::npos); EXPECT_TRUE(command_list.find("\"leases-reclaim\"") != string::npos); EXPECT_TRUE(command_list.find("\"libreload\"") != string::npos); - EXPECT_TRUE(command_list.find("\"config-set\"") != string::npos); EXPECT_TRUE(command_list.find("\"server-tag-get\"") != string::npos); EXPECT_TRUE(command_list.find("\"shutdown\"") != string::npos); EXPECT_TRUE(command_list.find("\"statistic-get\"") != string::npos); @@ -1071,6 +1105,7 @@ TEST_F(CtrlChannelDhcpv6SrvTest, commandsList) { // We expect the server to report at least the following commands: checkListCommands(rsp, "build-report"); checkListCommands(rsp, "config-get"); + checkListCommands(rsp, "config-reload"); checkListCommands(rsp, "config-set"); checkListCommands(rsp, "config-test"); checkListCommands(rsp, "config-write"); @@ -1177,7 +1212,7 @@ TEST_F(CtrlChannelDhcpv6SrvTest, configReloadBrokenFile) { // Although Kea is smart, its AI routines are not smart enough to handle // this one... at least not yet. ofstream f("test7.json", ios::trunc); - f << "gimme some addr, bro!"; + f << "gimme some addrs, bro!"; f.close(); // Now tell Kea to reload its config. @@ -1544,12 +1579,12 @@ TEST_F(CtrlChannelDhcpv6SrvTest, connectionTimeoutPartialCommand) { // Check that the server has signalled a timeout. EXPECT_EQ("{ \"result\": 1, \"text\": " - "\"Connection over control channel timed out," - " discarded partial command of 19 bytes\" }", response); + "\"Connection over control channel timed out, " + "discarded partial command of 19 bytes\" }", response); } // This test verifies that the server signals timeout if the transmission -// takes too long, having received no data. +// takes too long, having received no data from the client. TEST_F(CtrlChannelDhcpv6SrvTest, connectionTimeoutNoData) { createUnixChannelServer();