ctrl_dhcp4_srv_unittest.cc 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405
  1. // Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. #include <config.h>
  7. #include <asiolink/interval_timer.h>
  8. #include <asiolink/io_service.h>
  9. #include <cc/command_interpreter.h>
  10. #include <config/command_mgr.h>
  11. #include <dhcp/dhcp4.h>
  12. #include <dhcp4/ctrl_dhcp4_srv.h>
  13. #include <dhcp4/tests/dhcp4_test_utils.h>
  14. #include <dhcpsrv/cfgmgr.h>
  15. #include <dhcpsrv/lease.h>
  16. #include <dhcpsrv/lease_mgr_factory.h>
  17. #include <hooks/hooks_manager.h>
  18. #include <log/logger_support.h>
  19. #include <stats/stats_mgr.h>
  20. #include <testutils/io_utils.h>
  21. #include <testutils/unix_control_client.h>
  22. #include "marker_file.h"
  23. #include "test_libraries.h"
  24. #include <boost/scoped_ptr.hpp>
  25. #include <gtest/gtest.h>
  26. #include <cstdlib>
  27. #include <fstream>
  28. #include <iomanip>
  29. #include <iostream>
  30. #include <sstream>
  31. #include <thread>
  32. #include <arpa/inet.h>
  33. #include <unistd.h>
  34. using namespace std;
  35. using namespace isc;
  36. using namespace isc::asiolink;
  37. using namespace isc::config;
  38. using namespace isc::data;
  39. using namespace isc::dhcp;
  40. using namespace isc::dhcp::test;
  41. using namespace isc::hooks;
  42. using namespace isc::stats;
  43. using namespace isc::test;
  44. namespace {
  45. /// @brief Simple RAII class which stops IO service upon destruction
  46. /// of the object.
  47. class IOServiceWork {
  48. public:
  49. /// @brief Constructor.
  50. ///
  51. /// @param io_service Pointer to the IO service to be stopped.
  52. explicit IOServiceWork(const IOServicePtr& io_service)
  53. : io_service_(io_service) {
  54. }
  55. /// @brief Destructor.
  56. ///
  57. /// Stops IO service.
  58. ~IOServiceWork() {
  59. io_service_->stop();
  60. }
  61. private:
  62. /// @brief Pointer to the IO service to be stopped upon destruction.
  63. IOServicePtr io_service_;
  64. };
  65. class NakedControlledDhcpv4Srv: public ControlledDhcpv4Srv {
  66. // "Naked" DHCPv4 server, exposes internal fields
  67. public:
  68. NakedControlledDhcpv4Srv():ControlledDhcpv4Srv(0) {
  69. CfgMgr::instance().setFamily(AF_INET);
  70. }
  71. /// Expose internal methods for the sake of testing
  72. using Dhcpv4Srv::receivePacket;
  73. };
  74. /// @brief Default control connection timeout.
  75. const size_t DEFAULT_CONNECTION_TIMEOUT = 10;
  76. /// @brief Fixture class intended for testin control channel in the DHCPv4Srv
  77. class CtrlChannelDhcpv4SrvTest : public ::testing::Test {
  78. public:
  79. /// @brief Path to the UNIX socket being used to communicate with the server
  80. std::string socket_path_;
  81. /// @brief Pointer to the tested server object
  82. boost::shared_ptr<NakedControlledDhcpv4Srv> server_;
  83. /// @brief Default constructor
  84. ///
  85. /// Sets socket path to its default value.
  86. CtrlChannelDhcpv4SrvTest() {
  87. const char* env = getenv("KEA_SOCKET_TEST_DIR");
  88. if (env) {
  89. socket_path_ = string(env) + "/kea4.sock";
  90. } else {
  91. socket_path_ = string(TEST_DATA_BUILDDIR) + "/kea4.sock";
  92. }
  93. reset();
  94. }
  95. /// @brief Destructor
  96. ~CtrlChannelDhcpv4SrvTest() {
  97. LeaseMgrFactory::destroy();
  98. StatsMgr::instance().removeAll();
  99. CommandMgr::instance().closeCommandSocket();
  100. CommandMgr::instance().deregisterAll();
  101. CommandMgr::instance().setConnectionTimeout(DEFAULT_CONNECTION_TIMEOUT);
  102. server_.reset();
  103. };
  104. /// @brief Returns pointer to the server's IO service.
  105. ///
  106. /// @return Pointer to the server's IO service or null pointer if the server
  107. /// hasn't been created.
  108. IOServicePtr getIOService() {
  109. return (server_ ? server_->getIOService() : IOServicePtr());
  110. }
  111. void createUnixChannelServer() {
  112. ::remove(socket_path_.c_str());
  113. // Just a simple config. The important part here is the socket
  114. // location information.
  115. std::string header =
  116. "{"
  117. " \"interfaces-config\": {"
  118. " \"interfaces\": [ \"*\" ]"
  119. " },"
  120. " \"expired-leases-processing\": {"
  121. " \"reclaim-timer-wait-time\": 60,"
  122. " \"hold-reclaimed-time\": 500,"
  123. " \"flush-reclaimed-timer-wait-time\": 60"
  124. " },"
  125. " \"rebind-timer\": 2000, "
  126. " \"renew-timer\": 1000, "
  127. " \"subnet4\": [ ],"
  128. " \"valid-lifetime\": 4000,"
  129. " \"control-socket\": {"
  130. " \"socket-type\": \"unix\","
  131. " \"socket-name\": \"";
  132. std::string footer =
  133. "\" },"
  134. " \"lease-database\": {"
  135. " \"type\": \"memfile\", \"persist\": false }"
  136. "}";
  137. // Fill in the socket-name value with socket_path_ to
  138. // make the actual configuration text.
  139. std::string config_txt = header + socket_path_ + footer;
  140. ASSERT_NO_THROW(server_.reset(new NakedControlledDhcpv4Srv()));
  141. ConstElementPtr config;
  142. ASSERT_NO_THROW(config = parseDHCP4(config_txt));
  143. ConstElementPtr answer = server_->processConfig(config);
  144. // Commit the configuration so any subsequent reconfigurations
  145. // will only close the command channel if its configuration has
  146. // changed.
  147. CfgMgr::instance().commit();
  148. ASSERT_TRUE(answer);
  149. int status = 0;
  150. ConstElementPtr txt = isc::config::parseAnswer(status, answer);
  151. // This should succeed. If not, print the error message.
  152. ASSERT_EQ(0, status) << txt->str();
  153. // Now check that the socket was indeed open.
  154. ASSERT_GT(isc::config::CommandMgr::instance().getControlSocketFD(), -1);
  155. }
  156. /// @brief Reset hooks data
  157. ///
  158. /// Resets the data for the hooks-related portion of the test by ensuring
  159. /// that no libraries are loaded and that any marker files are deleted.
  160. void reset() {
  161. // Unload any previously-loaded libraries.
  162. HooksManager::unloadLibraries();
  163. // Get rid of any marker files.
  164. static_cast<void>(remove(LOAD_MARKER_FILE));
  165. static_cast<void>(remove(UNLOAD_MARKER_FILE));
  166. IfaceMgr::instance().deleteAllExternalSockets();
  167. CfgMgr::instance().clear();
  168. // Remove unix socket file
  169. ::remove(socket_path_.c_str());
  170. }
  171. /// @brief Conducts a command/response exchange via UnixCommandSocket
  172. ///
  173. /// This method connects to the given server over the given socket path.
  174. /// If successful, it then sends the given command and retrieves the
  175. /// server's response. Note that it calls the server's receivePacket()
  176. /// method where needed to cause the server to process IO events on
  177. /// control channel the control channel sockets.
  178. ///
  179. /// @param command the command text to execute in JSON form
  180. /// @param response variable into which the received response should be
  181. /// placed.
  182. void sendUnixCommand(const std::string& command, std::string& response) {
  183. response = "";
  184. boost::scoped_ptr<UnixControlClient> client;
  185. client.reset(new UnixControlClient());
  186. ASSERT_TRUE(client);
  187. // Connect to the server. This is expected to trigger server's acceptor
  188. // handler when IOService::poll() is run.
  189. ASSERT_TRUE(client->connectToServer(socket_path_));
  190. ASSERT_NO_THROW(getIOService()->poll());
  191. // Send the command. This will trigger server's handler which receives
  192. // data over the unix domain socket. The server will start sending
  193. // response to the client.
  194. ASSERT_TRUE(client->sendCommand(command));
  195. ASSERT_NO_THROW(getIOService()->poll());
  196. // Read the response generated by the server. Note that getResponse
  197. // only fails if there an IO error or no response data was present.
  198. // It is not based on the response content.
  199. ASSERT_TRUE(client->getResponse(response));
  200. // Now disconnect and process the close event
  201. client->disconnectFromServer();
  202. ASSERT_NO_THROW(getIOService()->poll());
  203. }
  204. /// @brief Checks response for list-commands
  205. ///
  206. /// This method checks if the list-commands response is generally sane
  207. /// and whether specified command is mentioned in the response.
  208. ///
  209. /// @param rsp response sent back by the server
  210. /// @param command command expected to be on the list.
  211. void checkListCommands(const ConstElementPtr& rsp, const std::string& command) {
  212. ConstElementPtr params;
  213. int status_code = -1;
  214. EXPECT_NO_THROW(params = parseAnswer(status_code, rsp));
  215. EXPECT_EQ(CONTROL_RESULT_SUCCESS, status_code);
  216. ASSERT_TRUE(params);
  217. ASSERT_EQ(Element::list, params->getType());
  218. int cnt = 0;
  219. for (size_t i = 0; i < params->size(); ++i) {
  220. string tmp = params->get(i)->stringValue();
  221. if (tmp == command) {
  222. // Command found, but that's not enough. Need to continue working
  223. // through the list to see if there are no duplicates.
  224. cnt++;
  225. }
  226. }
  227. // Exactly one command on the list is expected.
  228. EXPECT_EQ(1, cnt) << "Command " << command << " not found";
  229. }
  230. /// @brief Check if the answer for write-config command is correct
  231. ///
  232. /// @param response_txt response in text form (as read from the control socket)
  233. /// @param exp_status expected status (0 success, 1 failure)
  234. /// @param exp_txt for success cases this defines the expected filename,
  235. /// for failure cases this defines the expected error message
  236. void checkConfigWrite(const std::string& response_txt, int exp_status,
  237. const std::string& exp_txt = "") {
  238. ConstElementPtr rsp;
  239. EXPECT_NO_THROW(rsp = Element::fromJSON(response_txt));
  240. ASSERT_TRUE(rsp);
  241. int status;
  242. ConstElementPtr params = parseAnswer(status, rsp);
  243. EXPECT_EQ(exp_status, status);
  244. if (exp_status == CONTROL_RESULT_SUCCESS) {
  245. // Let's check couple things...
  246. // The parameters must include filename
  247. ASSERT_TRUE(params);
  248. ASSERT_TRUE(params->get("filename"));
  249. ASSERT_EQ(Element::string, params->get("filename")->getType());
  250. EXPECT_EQ(exp_txt, params->get("filename")->stringValue());
  251. // The parameters must include size. And the size
  252. // must indicate some content.
  253. ASSERT_TRUE(params->get("size"));
  254. ASSERT_EQ(Element::integer, params->get("size")->getType());
  255. int64_t size = params->get("size")->intValue();
  256. EXPECT_LE(1, size);
  257. // Now check if the file is really there and suitable for
  258. // opening.
  259. ifstream f(exp_txt, ios::binary | ios::ate);
  260. ASSERT_TRUE(f.good());
  261. // Now check that it is the correct size as reported.
  262. EXPECT_EQ(size, static_cast<int64_t>(f.tellg()));
  263. // Finally, check that it's really a JSON.
  264. ElementPtr from_file = Element::fromJSONFile(exp_txt);
  265. ASSERT_TRUE(from_file);
  266. } else if (exp_status == CONTROL_RESULT_ERROR) {
  267. // Let's check if the reason for failure was given.
  268. ConstElementPtr text = rsp->get("text");
  269. ASSERT_TRUE(text);
  270. ASSERT_EQ(Element::string, text->getType());
  271. EXPECT_EQ(exp_txt, text->stringValue());
  272. } else {
  273. ADD_FAILURE() << "Invalid expected status: " << exp_status;
  274. }
  275. }
  276. /// @brief Handler for long command.
  277. ///
  278. /// It checks whether the received command is equal to the one specified
  279. /// as an argument.
  280. ///
  281. /// @param expected_command String representing an expected command.
  282. /// @param command_name Command name received by the handler.
  283. /// @param arguments Command arguments received by the handler.
  284. ///
  285. /// @returns Success answer.
  286. static ConstElementPtr
  287. longCommandHandler(const std::string& expected_command,
  288. const std::string& command_name,
  289. const ConstElementPtr& arguments) {
  290. // The handler is called with a command name and the structure holding
  291. // command arguments. We have to rebuild the command from those
  292. // two arguments so as it can be compared against expected_command.
  293. ElementPtr entire_command = Element::createMap();
  294. entire_command->set("command", Element::create(command_name));
  295. entire_command->set("arguments", (arguments));
  296. // The rebuilt command will have a different order of parameters so
  297. // let's parse expected_command back to JSON to guarantee that
  298. // both structures are built using the same order.
  299. EXPECT_EQ(Element::fromJSON(expected_command)->str(),
  300. entire_command->str());
  301. return (createAnswer(0, "long command received ok"));
  302. }
  303. /// @brief Command handler which generates long response
  304. ///
  305. /// This handler generates a large response (over 400kB). It includes
  306. /// a list of randomly generated strings to make sure that the test
  307. /// can catch out of order delivery.
  308. static ConstElementPtr longResponseHandler(const std::string&,
  309. const ConstElementPtr&) {
  310. // By seeding the generator with the constant value we will always
  311. // get the same sequence of generated strings.
  312. std::srand(1);
  313. ElementPtr arguments = Element::createList();
  314. for (unsigned i = 0; i < 40000; ++i) {
  315. std::ostringstream s;
  316. s << std::setw(10) << std::rand();
  317. arguments->add(Element::create(s.str()));
  318. }
  319. return (createAnswer(0, arguments));
  320. }
  321. };
  322. TEST_F(CtrlChannelDhcpv4SrvTest, commands) {
  323. ASSERT_NO_THROW(
  324. server_.reset(new NakedControlledDhcpv4Srv());
  325. );
  326. // Use empty parameters list
  327. ElementPtr params(new isc::data::MapElement());
  328. int rcode = -1;
  329. // Case 1: send bogus command
  330. ConstElementPtr result = ControlledDhcpv4Srv::processCommand("blah", params);
  331. ConstElementPtr comment = parseAnswer(rcode, result);
  332. EXPECT_EQ(1, rcode); // expect failure (no such command as blah)
  333. // Case 2: send shutdown command without any parameters
  334. result = ControlledDhcpv4Srv::processCommand("shutdown", params);
  335. comment = parseAnswer(rcode, result);
  336. EXPECT_EQ(0, rcode); // expect success
  337. const pid_t pid(getpid());
  338. ConstElementPtr x(new isc::data::IntElement(pid));
  339. params->set("pid", x);
  340. // Case 3: send shutdown command with 1 parameter: pid
  341. result = ControlledDhcpv4Srv::processCommand("shutdown", params);
  342. comment = parseAnswer(rcode, result);
  343. EXPECT_EQ(0, rcode); // expect success
  344. }
  345. // Check that the "libreload" command will reload libraries
  346. TEST_F(CtrlChannelDhcpv4SrvTest, libreload) {
  347. createUnixChannelServer();
  348. // Ensure no marker files to start with.
  349. ASSERT_FALSE(checkMarkerFileExists(LOAD_MARKER_FILE));
  350. ASSERT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
  351. // Load two libraries
  352. HookLibsCollection libraries;
  353. libraries.push_back(make_pair(CALLOUT_LIBRARY_1, ConstElementPtr()));
  354. libraries.push_back(make_pair(CALLOUT_LIBRARY_2, ConstElementPtr()));
  355. HooksManager::loadLibraries(libraries);
  356. // Check they are loaded.
  357. std::vector<std::string> loaded_libraries =
  358. HooksManager::getLibraryNames();
  359. ASSERT_TRUE(extractNames(libraries) == loaded_libraries);
  360. // ... which also included checking that the marker file created by the
  361. // load functions exists and holds the correct value (of "12" - the
  362. // first library appends "1" to the file, the second appends "2"). Also
  363. // check that the unload marker file does not yet exist.
  364. EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "12"));
  365. EXPECT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
  366. // Now execute the "libreload" command. This should cause the libraries
  367. // to unload and to reload.
  368. std::string response;
  369. sendUnixCommand("{ \"command\": \"libreload\" }", response);
  370. EXPECT_EQ("{ \"result\": 0, "
  371. "\"text\": \"Hooks libraries successfully reloaded.\" }"
  372. , response);
  373. // Check that the libraries have unloaded and reloaded. The libraries are
  374. // unloaded in the reverse order to which they are loaded. When they load,
  375. // they should append information to the loading marker file.
  376. EXPECT_TRUE(checkMarkerFile(UNLOAD_MARKER_FILE, "21"));
  377. EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "1212"));
  378. }
  379. // This test checks which commands are registered by the DHCPv4 server.
  380. TEST_F(CtrlChannelDhcpv4SrvTest, commandsRegistration) {
  381. ConstElementPtr list_cmds = createCommand("list-commands");
  382. ConstElementPtr answer;
  383. // By default the list should be empty (except the standard list-commands
  384. // supported by the CommandMgr itself)
  385. EXPECT_NO_THROW(answer = CommandMgr::instance().processCommand(list_cmds));
  386. ASSERT_TRUE(answer);
  387. ASSERT_TRUE(answer->get("arguments"));
  388. EXPECT_EQ("[ \"list-commands\" ]", answer->get("arguments")->str());
  389. // Created server should register several additional commands.
  390. ASSERT_NO_THROW(
  391. server_.reset(new NakedControlledDhcpv4Srv());
  392. );
  393. EXPECT_NO_THROW(answer = CommandMgr::instance().processCommand(list_cmds));
  394. ASSERT_TRUE(answer);
  395. ASSERT_TRUE(answer->get("arguments"));
  396. std::string command_list = answer->get("arguments")->str();
  397. EXPECT_TRUE(command_list.find("\"list-commands\"") != string::npos);
  398. EXPECT_TRUE(command_list.find("\"build-report\"") != string::npos);
  399. EXPECT_TRUE(command_list.find("\"config-get\"") != string::npos);
  400. EXPECT_TRUE(command_list.find("\"config-set\"") != string::npos);
  401. EXPECT_TRUE(command_list.find("\"config-write\"") != string::npos);
  402. EXPECT_TRUE(command_list.find("\"leases-reclaim\"") != string::npos);
  403. EXPECT_TRUE(command_list.find("\"libreload\"") != string::npos);
  404. EXPECT_TRUE(command_list.find("\"shutdown\"") != string::npos);
  405. EXPECT_TRUE(command_list.find("\"statistic-get\"") != string::npos);
  406. EXPECT_TRUE(command_list.find("\"statistic-get-all\"") != string::npos);
  407. EXPECT_TRUE(command_list.find("\"statistic-remove\"") != string::npos);
  408. EXPECT_TRUE(command_list.find("\"statistic-remove-all\"") != string::npos);
  409. EXPECT_TRUE(command_list.find("\"statistic-reset\"") != string::npos);
  410. EXPECT_TRUE(command_list.find("\"statistic-reset-all\"") != string::npos);
  411. EXPECT_TRUE(command_list.find("\"version-get\"") != string::npos);
  412. // Ok, and now delete the server. It should deregister its commands.
  413. server_.reset();
  414. // The list should be (almost) empty again.
  415. EXPECT_NO_THROW(answer = CommandMgr::instance().processCommand(list_cmds));
  416. ASSERT_TRUE(answer);
  417. ASSERT_TRUE(answer->get("arguments"));
  418. EXPECT_EQ("[ \"list-commands\" ]", answer->get("arguments")->str());
  419. }
  420. // Tests that the server properly responds to invalid commands sent
  421. // via ControlChannel
  422. TEST_F(CtrlChannelDhcpv4SrvTest, controlChannelNegative) {
  423. createUnixChannelServer();
  424. std::string response;
  425. sendUnixCommand("{ \"command\": \"bogus\" }", response);
  426. EXPECT_EQ("{ \"result\": 2,"
  427. " \"text\": \"'bogus' command not supported.\" }", response);
  428. sendUnixCommand("utter nonsense", response);
  429. EXPECT_EQ("{ \"result\": 1, "
  430. "\"text\": \"invalid first character u\" }",
  431. response);
  432. }
  433. // Tests that the server properly responds to shtudown command sent
  434. // via ControlChannel
  435. TEST_F(CtrlChannelDhcpv4SrvTest, controlChannelShutdown) {
  436. createUnixChannelServer();
  437. std::string response;
  438. sendUnixCommand("{ \"command\": \"shutdown\" }", response);
  439. EXPECT_EQ("{ \"result\": 0, \"text\": \"Shutting down.\" }",response);
  440. }
  441. // This test verifies that the DHCP server immediately reclaims expired
  442. // leases on leases-reclaim command
  443. TEST_F(CtrlChannelDhcpv4SrvTest, controlLeasesReclaim) {
  444. createUnixChannelServer();
  445. // Create expired leases. Leases are expired by 40 seconds ago
  446. // (valid lifetime = 60, cltt = now - 100).
  447. HWAddrPtr hwaddr0(new HWAddr(HWAddr::fromText("00:01:02:03:04:05")));
  448. Lease4Ptr lease0(new Lease4(IOAddress("10.0.0.1"), hwaddr0,
  449. ClientIdPtr(), 60, 10, 20,
  450. time(NULL) - 100, SubnetID(1)));
  451. HWAddrPtr hwaddr1(new HWAddr(HWAddr::fromText("01:02:03:04:05:06")));
  452. Lease4Ptr lease1(new Lease4(IOAddress("10.0.0.2"), hwaddr1,
  453. ClientIdPtr(), 60, 10, 20,
  454. time(NULL) - 100, SubnetID(1)));
  455. // Add leases to the database.
  456. LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
  457. ASSERT_NO_THROW(lease_mgr.addLease(lease0));
  458. ASSERT_NO_THROW(lease_mgr.addLease(lease1));
  459. // Make sure they have been added.
  460. ASSERT_TRUE(lease_mgr.getLease4(IOAddress("10.0.0.1")));
  461. ASSERT_TRUE(lease_mgr.getLease4(IOAddress("10.0.0.2")));
  462. // No arguments
  463. std::string response;
  464. sendUnixCommand("{ \"command\": \"leases-reclaim\" }", response);
  465. EXPECT_EQ("{ \"result\": 1, \"text\": "
  466. "\"Missing mandatory 'remove' parameter.\" }", response);
  467. // Bad argument name
  468. sendUnixCommand("{ \"command\": \"leases-reclaim\", "
  469. "\"arguments\": { \"reclaim\": true } }", response);
  470. EXPECT_EQ("{ \"result\": 1, \"text\": "
  471. "\"Missing mandatory 'remove' parameter.\" }", response);
  472. // Bad remove argument type
  473. sendUnixCommand("{ \"command\": \"leases-reclaim\", "
  474. "\"arguments\": { \"remove\": \"bogus\" } }", response);
  475. EXPECT_EQ("{ \"result\": 1, \"text\": "
  476. "\"'remove' parameter expected to be a boolean.\" }", response);
  477. // Send the command
  478. sendUnixCommand("{ \"command\": \"leases-reclaim\", "
  479. "\"arguments\": { \"remove\": false } }", response);
  480. EXPECT_EQ("{ \"result\": 0, \"text\": "
  481. "\"Reclamation of expired leases is complete.\" }", response);
  482. // Leases should be reclaimed, but not removed
  483. ASSERT_NO_THROW(lease0 = lease_mgr.getLease4(IOAddress("10.0.0.1")));
  484. ASSERT_NO_THROW(lease1 = lease_mgr.getLease4(IOAddress("10.0.0.2")));
  485. ASSERT_TRUE(lease0);
  486. ASSERT_TRUE(lease1);
  487. EXPECT_TRUE(lease0->stateExpiredReclaimed());
  488. EXPECT_TRUE(lease1->stateExpiredReclaimed());
  489. }
  490. // This test verifies that the DHCP server handles version-get commands
  491. TEST_F(CtrlChannelDhcpv4SrvTest, getversion) {
  492. createUnixChannelServer();
  493. std::string response;
  494. // Send the version-get command
  495. sendUnixCommand("{ \"command\": \"version-get\" }", response);
  496. EXPECT_TRUE(response.find("\"result\": 0") != string::npos);
  497. EXPECT_TRUE(response.find("log4cplus") != string::npos);
  498. EXPECT_FALSE(response.find("GTEST_VERSION") != string::npos);
  499. // Send the build-report command
  500. sendUnixCommand("{ \"command\": \"build-report\" }", response);
  501. EXPECT_TRUE(response.find("\"result\": 0") != string::npos);
  502. EXPECT_TRUE(response.find("GTEST_VERSION") != string::npos);
  503. }
  504. // This test verifies that the DHCP server immediately removed expired
  505. // This test verifies that the DHCP server immediately removed expired
  506. // leases on leases-reclaim command with remove = true
  507. TEST_F(CtrlChannelDhcpv4SrvTest, controlLeasesReclaimRemove) {
  508. createUnixChannelServer();
  509. // Create expired leases. Leases are expired by 40 seconds ago
  510. // (valid lifetime = 60, cltt = now - 100).
  511. HWAddrPtr hwaddr0(new HWAddr(HWAddr::fromText("00:01:02:03:04:05")));
  512. Lease4Ptr lease0(new Lease4(IOAddress("10.0.0.1"), hwaddr0,
  513. ClientIdPtr(), 60, 10, 20,
  514. time(NULL) - 100, SubnetID(1)));
  515. HWAddrPtr hwaddr1(new HWAddr(HWAddr::fromText("01:02:03:04:05:06")));
  516. Lease4Ptr lease1(new Lease4(IOAddress("10.0.0.2"), hwaddr1,
  517. ClientIdPtr(), 60, 10, 20,
  518. time(NULL) - 100, SubnetID(1)));
  519. // Add leases to the database.
  520. LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
  521. ASSERT_NO_THROW(lease_mgr.addLease(lease0));
  522. ASSERT_NO_THROW(lease_mgr.addLease(lease1));
  523. // Make sure they have been added.
  524. ASSERT_TRUE(lease_mgr.getLease4(IOAddress("10.0.0.1")));
  525. ASSERT_TRUE(lease_mgr.getLease4(IOAddress("10.0.0.2")));
  526. // Send the command
  527. std::string response;
  528. sendUnixCommand("{ \"command\": \"leases-reclaim\", "
  529. "\"arguments\": { \"remove\": true } }", response);
  530. EXPECT_EQ("{ \"result\": 0, \"text\": "
  531. "\"Reclamation of expired leases is complete.\" }", response);
  532. // Leases should have been removed.
  533. ASSERT_NO_THROW(lease0 = lease_mgr.getLease4(IOAddress("10.0.0.1")));
  534. ASSERT_NO_THROW(lease1 = lease_mgr.getLease4(IOAddress("10.0.0.2")));
  535. EXPECT_FALSE(lease0);
  536. EXPECT_FALSE(lease1);
  537. }
  538. // Tests that the server properly responds to statistics commands. Note this
  539. // is really only intended to verify that the appropriate Statistics handler
  540. // is called based on the command. It is not intended to be an exhaustive
  541. // test of Dhcpv4 statistics.
  542. TEST_F(CtrlChannelDhcpv4SrvTest, controlChannelStats) {
  543. createUnixChannelServer();
  544. std::string response;
  545. // Check statistic-get
  546. sendUnixCommand("{ \"command\" : \"statistic-get\", "
  547. " \"arguments\": {"
  548. " \"name\":\"bogus\" }}", response);
  549. EXPECT_EQ("{ \"arguments\": { }, \"result\": 0 }", response);
  550. // Check statistic-get-all
  551. sendUnixCommand("{ \"command\" : \"statistic-get-all\", "
  552. " \"arguments\": {}}", response);
  553. EXPECT_EQ("{ \"arguments\": { }, \"result\": 0 }", response);
  554. // Check statistic-reset
  555. sendUnixCommand("{ \"command\" : \"statistic-reset\", "
  556. " \"arguments\": {"
  557. " \"name\":\"bogus\" }}", response);
  558. EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }",
  559. response);
  560. // Check statistic-reset-all
  561. sendUnixCommand("{ \"command\" : \"statistic-reset-all\", "
  562. " \"arguments\": {}}", response);
  563. EXPECT_EQ("{ \"result\": 0, \"text\": "
  564. "\"All statistics reset to neutral values.\" }", response);
  565. // Check statistic-remove
  566. sendUnixCommand("{ \"command\" : \"statistic-remove\", "
  567. " \"arguments\": {"
  568. " \"name\":\"bogus\" }}", response);
  569. EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }",
  570. response);
  571. // Check statistic-remove-all
  572. sendUnixCommand("{ \"command\" : \"statistic-remove-all\", "
  573. " \"arguments\": {}}", response);
  574. EXPECT_EQ("{ \"result\": 0, \"text\": \"All statistics removed.\" }",
  575. response);
  576. }
  577. // Check that the "config-set" command will replace current configuration
  578. TEST_F(CtrlChannelDhcpv4SrvTest, configSet) {
  579. createUnixChannelServer();
  580. // Define strings to permutate the config arguments
  581. // (Note the line feeds makes errors easy to find)
  582. string set_config_txt = "{ \"command\": \"config-set\" \n";
  583. string args_txt = " \"arguments\": { \n";
  584. string dhcp4_cfg_txt =
  585. " \"Dhcp4\": { \n"
  586. " \"interfaces-config\": { \n"
  587. " \"interfaces\": [\"*\"] \n"
  588. " }, \n"
  589. " \"valid-lifetime\": 4000, \n"
  590. " \"renew-timer\": 1000, \n"
  591. " \"rebind-timer\": 2000, \n"
  592. " \"lease-database\": { \n"
  593. " \"type\": \"memfile\", \n"
  594. " \"persist\":false, \n"
  595. " \"lfc-interval\": 0 \n"
  596. " }, \n"
  597. " \"expired-leases-processing\": { \n"
  598. " \"reclaim-timer-wait-time\": 0, \n"
  599. " \"hold-reclaimed-time\": 0, \n"
  600. " \"flush-reclaimed-timer-wait-time\": 0 \n"
  601. " },"
  602. " \"subnet4\": [ \n";
  603. string subnet1 =
  604. " {\"subnet\": \"192.2.0.0/24\", \n"
  605. " \"pools\": [{ \"pool\": \"192.2.0.1-192.2.0.50\" }]}\n";
  606. string subnet2 =
  607. " {\"subnet\": \"192.2.1.0/24\", \n"
  608. " \"pools\": [{ \"pool\": \"192.2.1.1-192.2.1.50\" }]}\n";
  609. string bad_subnet =
  610. " {\"BOGUS\": \"192.2.2.0/24\", \n"
  611. " \"pools\": [{ \"pool\": \"192.2.2.1-192.2.2.50\" }]}\n";
  612. string subnet_footer =
  613. " ] \n";
  614. string control_socket_header =
  615. " ,\"control-socket\": { \n"
  616. " \"socket-type\": \"unix\", \n"
  617. " \"socket-name\": \"";
  618. string control_socket_footer =
  619. "\" \n} \n";
  620. string logger_txt =
  621. " \"Logging\": { \n"
  622. " \"loggers\": [ { \n"
  623. " \"name\": \"kea\", \n"
  624. " \"severity\": \"FATAL\", \n"
  625. " \"output_options\": [{ \n"
  626. " \"output\": \"/dev/null\" \n"
  627. " }] \n"
  628. " }] \n"
  629. " } \n";
  630. std::ostringstream os;
  631. // Create a valid config with all the parts should parse
  632. os << set_config_txt << ","
  633. << args_txt
  634. << dhcp4_cfg_txt
  635. << subnet1
  636. << subnet_footer
  637. << control_socket_header
  638. << socket_path_
  639. << control_socket_footer
  640. << "}\n" // close dhcp4
  641. << ","
  642. << logger_txt
  643. << "}}";
  644. // Send the config-set command
  645. std::string response;
  646. sendUnixCommand(os.str(), response);
  647. // Verify the configuration was successful.
  648. EXPECT_EQ("{ \"result\": 0, \"text\": \"Configuration successful.\" }",
  649. response);
  650. // Check that the config was indeed applied.
  651. const Subnet4Collection* subnets =
  652. CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
  653. EXPECT_EQ(1, subnets->size());
  654. // Create a config with malformed subnet that should fail to parse.
  655. os.str("");
  656. os << set_config_txt << ","
  657. << args_txt
  658. << dhcp4_cfg_txt
  659. << bad_subnet
  660. << subnet_footer
  661. << control_socket_header
  662. << socket_path_
  663. << control_socket_footer
  664. << "}\n" // close dhcp4
  665. "}}";
  666. // Send the config-set command
  667. sendUnixCommand(os.str(), response);
  668. // Should fail with a syntax error
  669. EXPECT_EQ("{ \"result\": 1, "
  670. "\"text\": \"subnet configuration failed: mandatory 'subnet' "
  671. "parameter is missing for a subnet being configured (<wire>:19:17)\" }",
  672. response);
  673. // Check that the config was not lost
  674. subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
  675. EXPECT_EQ(1, subnets->size());
  676. // Create a valid config with two subnets and no command channel.
  677. // It should succeed, client should still receive the response
  678. os.str("");
  679. os << set_config_txt << ","
  680. << args_txt
  681. << dhcp4_cfg_txt
  682. << subnet1
  683. << ",\n"
  684. << subnet2
  685. << subnet_footer
  686. << "}\n" // close dhcp4
  687. << "}}";
  688. // Verify the control channel socket exists.
  689. ASSERT_TRUE(fileExists(socket_path_));
  690. // Send the config-set command.
  691. sendUnixCommand(os.str(), response);
  692. // Verify the control channel socket no longer exists.
  693. EXPECT_FALSE(fileExists(socket_path_));
  694. // With no command channel, should still receive the response.
  695. EXPECT_EQ("{ \"result\": 0, \"text\": \"Configuration successful.\" }",
  696. response);
  697. // Check that the config was not lost
  698. subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
  699. EXPECT_EQ(2, subnets->size());
  700. // Clean up after the test.
  701. CfgMgr::instance().clear();
  702. }
  703. // Tests that the server properly responds to shtudown command sent
  704. // via ControlChannel
  705. TEST_F(CtrlChannelDhcpv4SrvTest, listCommands) {
  706. createUnixChannelServer();
  707. std::string response;
  708. sendUnixCommand("{ \"command\": \"list-commands\" }", response);
  709. ConstElementPtr rsp;
  710. EXPECT_NO_THROW(rsp = Element::fromJSON(response));
  711. // We expect the server to report at least the following commands:
  712. checkListCommands(rsp, "build-report");
  713. checkListCommands(rsp, "config-get");
  714. checkListCommands(rsp, "config-reload");
  715. checkListCommands(rsp, "config-set");
  716. checkListCommands(rsp, "config-write");
  717. checkListCommands(rsp, "list-commands");
  718. checkListCommands(rsp, "leases-reclaim");
  719. checkListCommands(rsp, "libreload");
  720. checkListCommands(rsp, "shutdown");
  721. checkListCommands(rsp, "statistic-get");
  722. checkListCommands(rsp, "statistic-get-all");
  723. checkListCommands(rsp, "statistic-remove");
  724. checkListCommands(rsp, "statistic-remove-all");
  725. checkListCommands(rsp, "statistic-reset");
  726. checkListCommands(rsp, "statistic-reset-all");
  727. checkListCommands(rsp, "version-get");
  728. }
  729. // Tests if the server returns its configuration using config-get.
  730. // Note there are separate tests that verify if toElement() called by the
  731. // config-get handler are actually converting the configuration correctly.
  732. TEST_F(CtrlChannelDhcpv4SrvTest, configGet) {
  733. createUnixChannelServer();
  734. std::string response;
  735. sendUnixCommand("{ \"command\": \"config-get\" }", response);
  736. ConstElementPtr rsp;
  737. // The response should be a valid JSON.
  738. EXPECT_NO_THROW(rsp = Element::fromJSON(response));
  739. ASSERT_TRUE(rsp);
  740. int status;
  741. ConstElementPtr cfg = parseAnswer(status, rsp);
  742. EXPECT_EQ(CONTROL_RESULT_SUCCESS, status);
  743. // Ok, now roughly check if the response seems legit.
  744. ASSERT_TRUE(cfg);
  745. ASSERT_EQ(Element::map, cfg->getType());
  746. EXPECT_TRUE(cfg->get("Dhcp4"));
  747. }
  748. // Verify that the "config-test" command will do what we expect.
  749. TEST_F(CtrlChannelDhcpv4SrvTest, configTest) {
  750. createUnixChannelServer();
  751. // Define strings to permutate the config arguments
  752. // (Note the line feeds makes errors easy to find)
  753. string set_config_txt = "{ \"command\": \"config-set\" \n";
  754. string config_test_txt = "{ \"command\": \"config-test\" \n";
  755. string args_txt = " \"arguments\": { \n";
  756. string dhcp4_cfg_txt =
  757. " \"Dhcp4\": { \n"
  758. " \"interfaces-config\": { \n"
  759. " \"interfaces\": [\"*\"] \n"
  760. " }, \n"
  761. " \"valid-lifetime\": 4000, \n"
  762. " \"renew-timer\": 1000, \n"
  763. " \"rebind-timer\": 2000, \n"
  764. " \"lease-database\": { \n"
  765. " \"type\": \"memfile\", \n"
  766. " \"persist\":false, \n"
  767. " \"lfc-interval\": 0 \n"
  768. " }, \n"
  769. " \"expired-leases-processing\": { \n"
  770. " \"reclaim-timer-wait-time\": 0, \n"
  771. " \"hold-reclaimed-time\": 0, \n"
  772. " \"flush-reclaimed-timer-wait-time\": 0 \n"
  773. " },"
  774. " \"subnet4\": [ \n";
  775. string subnet1 =
  776. " {\"subnet\": \"192.2.0.0/24\", \n"
  777. " \"pools\": [{ \"pool\": \"192.2.0.1-192.2.0.50\" }]}\n";
  778. string subnet2 =
  779. " {\"subnet\": \"192.2.1.0/24\", \n"
  780. " \"pools\": [{ \"pool\": \"192.2.1.1-192.2.1.50\" }]}\n";
  781. string bad_subnet =
  782. " {\"BOGUS\": \"192.2.2.0/24\", \n"
  783. " \"pools\": [{ \"pool\": \"192.2.2.1-192.2.2.50\" }]}\n";
  784. string subnet_footer =
  785. " ] \n";
  786. string control_socket_header =
  787. " ,\"control-socket\": { \n"
  788. " \"socket-type\": \"unix\", \n"
  789. " \"socket-name\": \"";
  790. string control_socket_footer =
  791. "\" \n} \n";
  792. string logger_txt =
  793. " \"Logging\": { \n"
  794. " \"loggers\": [ { \n"
  795. " \"name\": \"kea\", \n"
  796. " \"severity\": \"FATAL\", \n"
  797. " \"output_options\": [{ \n"
  798. " \"output\": \"/dev/null\" \n"
  799. " }] \n"
  800. " }] \n"
  801. " } \n";
  802. std::ostringstream os;
  803. // Create a valid config with all the parts should parse
  804. os << set_config_txt << ","
  805. << args_txt
  806. << dhcp4_cfg_txt
  807. << subnet1
  808. << subnet_footer
  809. << control_socket_header
  810. << socket_path_
  811. << control_socket_footer
  812. << "}\n" // close dhcp4
  813. << ","
  814. << logger_txt
  815. << "}}";
  816. // Send the config-set command
  817. std::string response;
  818. sendUnixCommand(os.str(), response);
  819. // Verify the configuration was successful.
  820. EXPECT_EQ("{ \"result\": 0, \"text\": \"Configuration successful.\" }",
  821. response);
  822. // Check that the config was indeed applied.
  823. const Subnet4Collection* subnets =
  824. CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
  825. EXPECT_EQ(1, subnets->size());
  826. // Create a config with malformed subnet that should fail to parse.
  827. os.str("");
  828. os << config_test_txt << ","
  829. << args_txt
  830. << dhcp4_cfg_txt
  831. << bad_subnet
  832. << subnet_footer
  833. << control_socket_header
  834. << socket_path_
  835. << control_socket_footer
  836. << "}\n" // close dhcp4
  837. "}}";
  838. // Send the config-test command
  839. sendUnixCommand(os.str(), response);
  840. // Should fail with a syntax error
  841. EXPECT_EQ("{ \"result\": 1, "
  842. "\"text\": \"subnet configuration failed: mandatory 'subnet' "
  843. "parameter is missing for a subnet being configured (<wire>:19:17)\" }",
  844. response);
  845. // Check that the config was not lost
  846. subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
  847. EXPECT_EQ(1, subnets->size());
  848. // Create a valid config with two subnets and no command channel.
  849. os.str("");
  850. os << config_test_txt << ","
  851. << args_txt
  852. << dhcp4_cfg_txt
  853. << subnet1
  854. << ",\n"
  855. << subnet2
  856. << subnet_footer
  857. << "}\n" // close dhcp4
  858. << "}}";
  859. // Verify the control channel socket exists.
  860. ASSERT_TRUE(fileExists(socket_path_));
  861. // Send the config-test command
  862. sendUnixCommand(os.str(), response);
  863. // Verify the control channel socket still exists.
  864. EXPECT_TRUE(fileExists(socket_path_));
  865. // Verify the configuration was successful.
  866. EXPECT_EQ("{ \"result\": 0, \"text\": \"Configuration seems sane. "
  867. "Control-socket, hook-libraries, and D2 configuration were "
  868. "sanity checked, but not applied.\" }",
  869. response);
  870. // Check that the config was not applied
  871. subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
  872. EXPECT_EQ(1, subnets->size());
  873. // Clean up after the test.
  874. CfgMgr::instance().clear();
  875. }
  876. // Tests if config-write can be called without any parameters.
  877. TEST_F(CtrlChannelDhcpv4SrvTest, writeConfigNoFilename) {
  878. createUnixChannelServer();
  879. std::string response;
  880. // This is normally set by the command line -c parameter.
  881. server_->setConfigFile("test1.json");
  882. // If the filename is not explicitly specified, the name used
  883. // in -c command line switch is used.
  884. sendUnixCommand("{ \"command\": \"config-write\" }", response);
  885. checkConfigWrite(response, CONTROL_RESULT_SUCCESS, "test1.json");
  886. ::remove("test1.json");
  887. }
  888. // Tests if config-write can be called with a valid filename as parameter.
  889. TEST_F(CtrlChannelDhcpv4SrvTest, writeConfigFilename) {
  890. createUnixChannelServer();
  891. std::string response;
  892. sendUnixCommand("{ \"command\": \"config-write\", "
  893. "\"arguments\": { \"filename\": \"test2.json\" } }", response);
  894. checkConfigWrite(response, CONTROL_RESULT_SUCCESS, "test2.json");
  895. ::remove("test2.json");
  896. }
  897. // Tests if config-reload attempts to reload a file and reports that the
  898. // file is missing.
  899. TEST_F(CtrlChannelDhcpv4SrvTest, configReloadMissingFile) {
  900. createUnixChannelServer();
  901. std::string response;
  902. // This is normally set to whatever value is passed to -c when the server is
  903. // started, but we're not starting it that way, so need to set it by hand.
  904. server_->setConfigFile("test6.json");
  905. // Tell the server to reload its configuration. It should attempt to load
  906. // test6.json (and fail, because the file is not there).
  907. sendUnixCommand("{ \"command\": \"config-reload\" }", response);
  908. // Verify the reload was rejected.
  909. EXPECT_EQ("{ \"result\": 1, \"text\": \"Config reload failed:"
  910. "configuration error using file 'test6.json': Unable to open file "
  911. "test6.json\" }",
  912. response);
  913. }
  914. // Tests if config-reload attempts to reload a file and reports that the
  915. // file is not a valid JSON.
  916. TEST_F(CtrlChannelDhcpv4SrvTest, configReloadBrokenFile) {
  917. createUnixChannelServer();
  918. std::string response;
  919. // This is normally set to whatever value is passed to -c when the server is
  920. // started, but we're not starting it that way, so need to set it by hand.
  921. server_->setConfigFile("test7.json");
  922. // Although Kea is smart, its AI routines are not smart enough to handle
  923. // this one... at least not yet.
  924. ofstream f("test7.json", ios::trunc);
  925. f << "gimme some addrs, bro!";
  926. f.close();
  927. // Now tell Kea to reload its config.
  928. sendUnixCommand("{ \"command\": \"config-reload\" }", response);
  929. // Verify the reload will fail.
  930. EXPECT_EQ("{ \"result\": 1, \"text\": \"Config reload failed:"
  931. "configuration error using file 'test7.json': "
  932. "test7.json:1.1: Invalid character: g\" }",
  933. response);
  934. ::remove("test7.json");
  935. }
  936. // Tests if config-reload attempts to reload a file and reports that the
  937. // file is loaded correctly.
  938. TEST_F(CtrlChannelDhcpv4SrvTest, configReloadValid) {
  939. createUnixChannelServer();
  940. std::string response;
  941. // This is normally set to whatever value is passed to -c when the server is
  942. // started, but we're not starting it that way, so need to set it by hand.
  943. server_->setConfigFile("test8.json");
  944. // Ok, enough fooling around. Let's create a valid config.
  945. const std::string cfg_txt =
  946. "{ \"Dhcp4\": {"
  947. " \"interfaces-config\": {"
  948. " \"interfaces\": [ \"*\" ]"
  949. " },"
  950. " \"subnet4\": ["
  951. " { \"subnet\": \"192.0.2.0/24\" },"
  952. " { \"subnet\": \"192.0.3.0/24\" }"
  953. " ],"
  954. " \"valid-lifetime\": 4000,"
  955. " \"lease-database\": {"
  956. " \"type\": \"memfile\", \"persist\": false }"
  957. "} }";
  958. ofstream f("test8.json", ios::trunc);
  959. f << cfg_txt;
  960. f.close();
  961. // This command should reload test8.json config.
  962. sendUnixCommand("{ \"command\": \"config-reload\" }", response);
  963. // Verify the configuration was successful.
  964. EXPECT_EQ("{ \"result\": 0, \"text\": \"Configuration successful.\" }",
  965. response);
  966. // Check that the config was indeed applied.
  967. const Subnet4Collection* subnets =
  968. CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
  969. EXPECT_EQ(2, subnets->size());
  970. ::remove("test8.json");
  971. }
  972. /// Verify that concurrent connections over the control channel can be
  973. /// established.
  974. /// @todo Future Kea 1.3 tickets will modify the behavior of the CommandMgr
  975. /// such that the server will be able to send response in multiple chunks.
  976. /// This test will need to be extended. For now, the receive and write
  977. /// operations are atomic and there is no conflict between concurrent
  978. /// connections.
  979. TEST_F(CtrlChannelDhcpv4SrvTest, concurrentConnections) {
  980. createUnixChannelServer();
  981. boost::scoped_ptr<UnixControlClient> client1(new UnixControlClient());
  982. ASSERT_TRUE(client1);
  983. boost::scoped_ptr<UnixControlClient> client2(new UnixControlClient());
  984. ASSERT_TRUE(client2);
  985. // Client 1 connects.
  986. ASSERT_TRUE(client1->connectToServer(socket_path_));
  987. ASSERT_NO_THROW(getIOService()->poll());
  988. // Client 2 connects.
  989. ASSERT_TRUE(client2->connectToServer(socket_path_));
  990. ASSERT_NO_THROW(getIOService()->poll());
  991. // Send the command while another client is connected.
  992. ASSERT_TRUE(client2->sendCommand("{ \"command\": \"list-commands\" }"));
  993. ASSERT_NO_THROW(getIOService()->poll());
  994. std::string response;
  995. // The server should respond ok.
  996. ASSERT_TRUE(client2->getResponse(response));
  997. EXPECT_TRUE(response.find("\"result\": 0") != std::string::npos);
  998. // Disconnect the servers.
  999. client1->disconnectFromServer();
  1000. client2->disconnectFromServer();
  1001. ASSERT_NO_THROW(getIOService()->poll());
  1002. }
  1003. // This test verifies that the server can receive and process a large command.
  1004. TEST_F(CtrlChannelDhcpv4SrvTest, longCommand) {
  1005. std::ostringstream command;
  1006. // This is the desired size of the command sent to the server (1MB). The
  1007. // actual size sent will be slightly greater than that.
  1008. const size_t command_size = 1024 * 1000;
  1009. while (command.tellp() < command_size) {
  1010. // We're sending command 'foo' with arguments being a list of
  1011. // strings. If this is the first transmission, send command name
  1012. // and open the arguments list. Also insert the first argument
  1013. // so as all subsequent arguments can be prefixed with a comma.
  1014. if (command.tellp() == 0) {
  1015. command << "{ \"command\": \"foo\", \"arguments\": [ \"begin\"";
  1016. } else {
  1017. // Generate a random number and insert it into the stream as
  1018. // 10 digits long string.
  1019. std::ostringstream arg;
  1020. arg << setw(10) << std::rand();
  1021. // Append the argument in the command.
  1022. command << ", \"" << arg.str() << "\"\n";
  1023. // If we have hit the limit of the command size, close braces to
  1024. // get appropriate JSON.
  1025. if (command.tellp() > command_size) {
  1026. command << "] }";
  1027. }
  1028. }
  1029. }
  1030. ASSERT_NO_THROW(
  1031. CommandMgr::instance().registerCommand("foo",
  1032. boost::bind(&CtrlChannelDhcpv4SrvTest::longCommandHandler,
  1033. command.str(), _1, _2));
  1034. );
  1035. createUnixChannelServer();
  1036. std::string response;
  1037. std::thread th([this, &response, &command]() {
  1038. // IO service will be stopped automatically when this object goes
  1039. // out of scope and is destroyed. This is useful because we use
  1040. // asserts which may break the thread in various exit points.
  1041. IOServiceWork work(getIOService());
  1042. // Create client which we will use to send command to the server.
  1043. boost::scoped_ptr<UnixControlClient> client(new UnixControlClient());
  1044. ASSERT_TRUE(client);
  1045. // Connect to the server. This will trigger acceptor handler on the
  1046. // server side and create a new connection.
  1047. ASSERT_TRUE(client->connectToServer(socket_path_));
  1048. // Initially the remaining_string holds the entire command and we
  1049. // will be erasing the portions that we have sent.
  1050. std::string remaining_data = command.str();
  1051. while (!remaining_data.empty()) {
  1052. // Send the command in chunks of 1024 bytes.
  1053. const size_t l = remaining_data.size() < 1024 ? remaining_data.size() : 1024;
  1054. ASSERT_TRUE(client->sendCommand(remaining_data.substr(0, l)));
  1055. remaining_data.erase(0, l);
  1056. }
  1057. // Set timeout to 5 seconds to allow the time for the server to send
  1058. // a response.
  1059. const unsigned int timeout = 5;
  1060. ASSERT_TRUE(client->getResponse(response, timeout));
  1061. // We're done. Close the connection to the server.
  1062. client->disconnectFromServer();
  1063. });
  1064. // Run the server until the command has been processed and response
  1065. // received.
  1066. getIOService()->run();
  1067. // Wait for the thread to complete.
  1068. th.join();
  1069. EXPECT_EQ("{ \"result\": 0, \"text\": \"long command received ok\" }",
  1070. response);
  1071. }
  1072. // This test verifies that the server can send long response to the client.
  1073. TEST_F(CtrlChannelDhcpv4SrvTest, longResponse) {
  1074. // We need to generate large response. The simplest way is to create
  1075. // a command and a handler which will generate some static response
  1076. // of a desired size.
  1077. ASSERT_NO_THROW(
  1078. CommandMgr::instance().registerCommand("foo",
  1079. boost::bind(&CtrlChannelDhcpv4SrvTest::longResponseHandler, _1, _2));
  1080. );
  1081. createUnixChannelServer();
  1082. // The UnixControlClient doesn't have any means to check that the entire
  1083. // response has been received. What we want to do is to generate a
  1084. // reference response using our command handler and then compare
  1085. // what we have received over the unix domain socket with this reference
  1086. // response to figure out when to stop receiving.
  1087. std::string reference_response = longResponseHandler("foo", ConstElementPtr())->str();
  1088. // In this stream we're going to collect out partial responses.
  1089. std::ostringstream response;
  1090. // The client is synchronous so it is useful to run it in a thread.
  1091. std::thread th([this, &response, reference_response]() {
  1092. // IO service will be stopped automatically when this object goes
  1093. // out of scope and is destroyed. This is useful because we use
  1094. // asserts which may break the thread in various exit points.
  1095. IOServiceWork work(getIOService());
  1096. // Remember the response size so as we know when we should stop
  1097. // receiving.
  1098. const size_t long_response_size = reference_response.size();
  1099. // Create the client and connect it to the server.
  1100. boost::scoped_ptr<UnixControlClient> client(new UnixControlClient());
  1101. ASSERT_TRUE(client);
  1102. ASSERT_TRUE(client->connectToServer(socket_path_));
  1103. // Send the stub command.
  1104. std::string command = "{ \"command\": \"foo\", \"arguments\": { } }";
  1105. ASSERT_TRUE(client->sendCommand(command));
  1106. // Keep receiving response data until we have received the full answer.
  1107. while (response.tellp() < long_response_size) {
  1108. std::string partial;
  1109. const unsigned int timeout = 5;
  1110. ASSERT_TRUE(client->getResponse(partial, 5));
  1111. response << partial;
  1112. }
  1113. // We have received the entire response, so close the connection and
  1114. // stop the IO service.
  1115. client->disconnectFromServer();
  1116. });
  1117. // Run the server until the entire response has been received.
  1118. getIOService()->run();
  1119. // Wait for the thread to complete.
  1120. th.join();
  1121. // Make sure we have received correct response.
  1122. EXPECT_EQ(reference_response, response.str());
  1123. }
  1124. // This test verifies that the server signals timeout if the transmission
  1125. // takes too long.
  1126. TEST_F(CtrlChannelDhcpv4SrvTest, connectionTimeout) {
  1127. createUnixChannelServer();
  1128. // Set connection timeout to 2s to prevent long waiting time for the
  1129. // timeout during this test.
  1130. const unsigned short timeout = 2;
  1131. CommandMgr::instance().setConnectionTimeout(timeout);
  1132. // Server's response will be assigned to this variable.
  1133. std::string response;
  1134. // It is useful to create a thread and run the server and the client
  1135. // at the same time and independently.
  1136. std::thread th([this, &response]() {
  1137. // IO service will be stopped automatically when this object goes
  1138. // out of scope and is destroyed. This is useful because we use
  1139. // asserts which may break the thread in various exit points.
  1140. IOServiceWork work(getIOService());
  1141. // Create the client and connect it to the server.
  1142. boost::scoped_ptr<UnixControlClient> client(new UnixControlClient());
  1143. ASSERT_TRUE(client);
  1144. ASSERT_TRUE(client->connectToServer(socket_path_));
  1145. // Send partial command. The server will be waiting for the remaining
  1146. // part to be sent and will eventually signal a timeout.
  1147. std::string command = "{ \"command\": \"foo\" ";
  1148. ASSERT_TRUE(client->sendCommand(command));
  1149. // Let's wait up to 15s for the server's response. The response
  1150. // should arrive sooner assuming that the timeout mechanism for
  1151. // the server is working properly.
  1152. const unsigned int timeout = 15;
  1153. ASSERT_TRUE(client->getResponse(response, timeout));
  1154. // Explicitly close the client's connection.
  1155. client->disconnectFromServer();
  1156. });
  1157. // Run the server until stopped.
  1158. getIOService()->run();
  1159. // Wait for the thread to return.
  1160. th.join();
  1161. // Check that the server has signalled a timeout.
  1162. EXPECT_EQ("{ \"result\": 1, \"text\": \"Connection over control channel"
  1163. " timed out\" }", response);
  1164. }
  1165. } // End of anonymous namespace