diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.cc b/src/bin/dhcp4/ctrl_dhcp4_srv.cc index 3043bf3955..a66d56c54a 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.cc +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -19,7 +19,6 @@ #include #include #include -#include #include #include @@ -124,9 +123,31 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) { return (isc::config::createAnswer(1, err.str())); } - TimerMgr::instance()->stopThread(); + // We're going to modify the timers configuration. This is not allowed + // when the thread is running. + try { + TimerMgr::instance()->stopThread(); + } catch (const std::exception& ex) { + err << "Unable to stop worker thread running timers: " + << ex.what() << "."; + return (isc::config::createAnswer(1, err.str())); + } + ConstElementPtr answer = configureDhcp4Server(*srv, config); - TimerMgr::instance()->startThread(); + + // Start worker thread if there are any timers installed. Note that + // we also start worker thread when the reconfiguration failed, because + // in that case we continue using an old configuration and the server + // should still run installed timers. + if (TimerMgr::instance()->timersCount() > 0) { + try { + TimerMgr::instance()->startThread(); + } catch (const std::exception& ex) { + err << "Unable to start worker thread running timers: " + << ex.what() << "."; + return (isc::config::createAnswer(1, err.str())); + } + } // Check that configuration was successful. If not, do not reopen sockets // and don't bother with DDNS stuff. @@ -165,7 +186,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) { } ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t port /*= DHCP4_SERVER_PORT*/) - :Dhcpv4Srv(port) { + : Dhcpv4Srv(port), io_service_(), timer_mgr_(TimerMgr::instance()) { if (getInstance()) { isc_throw(InvalidOperation, "There is another Dhcpv4Srv instance already."); @@ -207,6 +228,9 @@ void ControlledDhcpv4Srv::shutdown() { ControlledDhcpv4Srv::~ControlledDhcpv4Srv() { cleanup(); + // Stop worker thread running timers, if it is running. + timer_mgr_->stopThread(); + // Close the command socket (if it exists). CommandMgr::instance().closeCommandSocket(); diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.h b/src/bin/dhcp4/ctrl_dhcp4_srv.h index 359ac15318..6defa25a73 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.h +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.h @@ -18,6 +18,7 @@ #include #include #include +#include #include namespace isc { @@ -102,13 +103,7 @@ public: } -protected: - /// @brief Static pointer to the sole instance of the DHCP server. - /// - /// This is required for config and command handlers to gain access to - /// the server - static ControlledDhcpv4Srv* server_; - +private: /// @brief Callback that will be called from iface_mgr when data /// is received over control socket. /// @@ -117,9 +112,6 @@ protected: /// (that was sent from some yet unspecified sender). static void sessionReader(void); - /// @brief IOService object, used for all ASIO operations. - isc::asiolink::IOService io_service_; - /// @brief Handler for processing 'shutdown' command /// /// This handler processes shutdown command, which initializes shutdown @@ -157,6 +149,21 @@ protected: isc::data::ConstElementPtr commandConfigReloadHandler(const std::string& command, isc::data::ConstElementPtr args); + + /// @brief Static pointer to the sole instance of the DHCP server. + /// + /// This is required for config and command handlers to gain access to + /// the server + static ControlledDhcpv4Srv* server_; + + /// @brief IOService object, used for all ASIO operations. + isc::asiolink::IOService io_service_; + + /// @brief Instance of the @c TimerMgr. + /// + /// Shared pointer to the instance of timer @c TimerMgr is held here to + /// make sure that the @c TimerMgr outlives instance of this class. + TimerMgrPtr timer_mgr_; }; }; // namespace isc::dhcp diff --git a/src/bin/dhcp4/json_config_parser.cc b/src/bin/dhcp4/json_config_parser.cc index 36e06cb749..22c45fd7c8 100644 --- a/src/bin/dhcp4/json_config_parser.cc +++ b/src/bin/dhcp4/json_config_parser.cc @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -461,6 +462,9 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) { // so newly recreated configuration starts with first subnet-id equal 1. Subnet::resetSubnetID(); + // Remove any existing timers. + TimerMgr::instance()->unregisterTimers(); + // Some of the values specified in the configuration depend on // other values. Typically, the values in the subnet4 structure // depend on the global values. Also, option values configuration diff --git a/src/bin/dhcp4/tests/dhcp4_process_tests.sh.in b/src/bin/dhcp4/tests/dhcp4_process_tests.sh.in index 5f8dab791d..6959c94e76 100755 --- a/src/bin/dhcp4/tests/dhcp4_process_tests.sh.in +++ b/src/bin/dhcp4/tests/dhcp4_process_tests.sh.in @@ -282,17 +282,17 @@ shutdown_test() { test_finish 0 } -# This test verifies that DHCPv4 can be reconfigured with a SIGHUP signal. +# This test verifies that DHCPv4 can be configured to run lease file cleanup +# periodially. lfc_timer_test() { # Log the start of the test and print test name. test_start "dhcpv4_srv.lfc_timer_test" # Remove dangling Kea instances and remove log files. cleanup # Create a configuration with the LFC enabled, by replacing the section - # with the lfc-interval parameter. + # with the lfc-interval and persist parameters. LFC_CONFIG=$(printf "${CONFIG}" | sed -e 's/\"lfc-interval\": 0/\"lfc-interval\": 1/g' \ | sed -e 's/\"persist\": false/\"persist\": true/g') - echo ${LFC_CONFIG} # Create new configuration file. create_config "${LFC_CONFIG}" # Instruct Kea to log to the specific file. @@ -315,13 +315,51 @@ lfc_timer_test() { clean_exit 1 fi + # Check if Kea emits the log message indicating that LFC is started. wait_for_message 10 "DHCPSRV_MEMFILE_LFC_EXECUTE" 1 if [ ${_WAIT_FOR_MESSAGE} -eq 0 ]; then printf "ERROR: Server did not execute LFC.\n" clean_exit 1 fi - sleep 3 + # Give it a short time to run. + sleep 1 + + # Modify the interval. + LFC_CONFIG=$(printf "${CONFIG}" | sed -e 's/\"lfc-interval\": 1/\"lfc-interval\": 2/g') + # Create new configuration file. + create_config "${LFC_CONFIG}" + + # Reconfigure the server with SIGHUP. + send_signal 1 ${bin} + + # There should be two occurrences of the DHCP4_CONFIG_COMPLETE messages. + # Wait for it up to 10s. + wait_for_message 10 "DHCP4_CONFIG_COMPLETE" 2 + + # After receiving SIGHUP the server should get reconfigured and the + # reconfiguration should be noted in the log file. We should now + # have two configurations logged in the log file. + if [ ${_WAIT_FOR_MESSAGE} -eq 0 ]; then + printf "ERROR: server hasn't been reconfigured.\n" + clean_exit 1 + else + printf "Server successfully reconfigured.\n" + fi + + # Make sure the server is still operational. + get_pids ${bin} + if [ ${_GET_PIDS_NUM} -ne 1 ]; then + printf "ERROR: Kea process was killed when attempting reconfiguration.\n" + clean_exit 1 + fi + + # Wait for the LFC to run the second time. + wait_for_message 10 "DHCPSRV_MEMFILE_LFC_EXECUTE" 2 + if [ ${_WAIT_FOR_MESSAGE} -eq 0 ]; then + printf "ERROR: Server did not execute LFC.\n" + clean_exit 1 + fi # Send signal to Kea SIGTERM send_signal 15 ${bin}