From: Marcin Siodelski Date: Thu, 22 May 2014 12:57:24 +0000 (+0200) Subject: [3406] Implemented dynamic reconfiguration when SIGHUP is received. X-Git-Tag: trac3434_base~17^2~12 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ba9bed21a4670928f065e928c12accbde02a6efe;p=thirdparty%2Fkea.git [3406] Implemented dynamic reconfiguration when SIGHUP is received. --- diff --git a/src/bin/dhcp6/bundy_controller.cc b/src/bin/dhcp6/bundy_controller.cc index 2a6b4092da..8b2d3338a9 100644 --- a/src/bin/dhcp6/bundy_controller.cc +++ b/src/bin/dhcp6/bundy_controller.cc @@ -133,7 +133,10 @@ bundyConfigHandler(ConstElementPtr new_config) { } void -ControlledDhcpv6Srv::init(const std::string& /* config_file*/) { +ControlledDhcpv6Srv::init(const std::string& config_file) { + // Call base class's init. + Daemon::init(config_file); + // This is Bundy configuration backed. It established control session // that is used to connect to Bundy framework. // diff --git a/src/bin/dhcp6/kea_controller.cc b/src/bin/dhcp6/kea_controller.cc index a5b357dd85..fab28b4fd2 100644 --- a/src/bin/dhcp6/kea_controller.cc +++ b/src/bin/dhcp6/kea_controller.cc @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -45,11 +46,18 @@ using namespace isc::log; using namespace isc::util; using namespace std; -namespace isc { -namespace dhcp { - -void -ControlledDhcpv6Srv::init(const std::string& file_name) { +namespace { + +/// @brief Configure DHCPv6 server using the configuration file specified. +/// +/// This function is used to both configure the DHCP server on its startup +/// and dynamically reconfigure the server when SIGHUP signal is received. +/// +/// It fetches DHCPv6 server's configuration from the 'Dhcp6' section of +/// the JSON configuration file. +/// +/// @param file_name Configuration file location. +void configure(const std::string& file_name) { // This is a configuration backend implementation that reads the // configuration from a JSON file. @@ -61,7 +69,7 @@ ControlledDhcpv6Srv::init(const std::string& file_name) { try { if (file_name.empty()) { // Basic sanity check: file name must not be empty. - isc_throw(BadValue, "JSON configuration file not specified. Please " + isc_throw(isc::BadValue, "JSON configuration file not specified. Please " "use -c command line option."); } @@ -71,7 +79,7 @@ ControlledDhcpv6Srv::init(const std::string& file_name) { if (!json) { LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL) .arg("Config file " + file_name + " missing or empty."); - isc_throw(BadValue, "Unable to process JSON configuration file:" + isc_throw(isc::BadValue, "Unable to process JSON configuration file:" + file_name); } @@ -81,16 +89,16 @@ ControlledDhcpv6Srv::init(const std::string& file_name) { if (!dhcp6) { LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL) .arg("Config file " + file_name + " does not include 'Dhcp6' entry."); - isc_throw(BadValue, "Unable to process JSON configuration file:" + isc_throw(isc::BadValue, "Unable to process JSON configuration file:" + file_name); } // Use parsed JSON structures to configure the server - result = processCommand("config-reload", dhcp6); + result = ControlledDhcpv6Srv::processCommand("config-reload", dhcp6); } catch (const std::exception& ex) { LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL).arg(ex.what()); - isc_throw(BadValue, "Unable to process JSON configuration file:" + isc_throw(isc::BadValue, "Unable to process JSON configuration file:" + file_name); } @@ -114,12 +122,42 @@ ControlledDhcpv6Srv::init(const std::string& file_name) { reason = string(" (") + comment->stringValue() + string(")"); } LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL).arg(reason); - isc_throw(BadValue, "Failed to apply configuration:" << reason); + isc_throw(isc::BadValue, "Failed to apply configuration:" << reason); } +} + +/// @brief Signals handler for DHCPv6 server. +/// +/// Currently, this function handles SIGHUP signal only. When this signal +/// is received it triggeres DHCSP server reconfiguration. +/// +/// @param signo Signal number received. +void signalHandler(int signo) { + if (signo == SIGHUP) { + configure(ControlledDhcpv6Srv::getInstance()->getConfigFile()); + } +} + +} + +namespace isc { +namespace dhcp { + +void +ControlledDhcpv6Srv::init(const std::string& file_name) { + // Call parent class's init to initialize file name. + Daemon::init(file_name); + + // Configure the server using JSON file. + configure(file_name); // We don't need to call openActiveSockets() or startD2() as these // methods are called in processConfig() which is called by // processCommand("reload-config", ...) + + // Set signal handler function for SIGHUP. When the SIGHUP is received + // by the process the server reconfiguration will be triggered. + signal(SIGHUP, signalHandler); } void ControlledDhcpv6Srv::cleanup() { diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am index d3c9224177..ed47eddd9a 100644 --- a/src/bin/dhcp6/tests/Makefile.am +++ b/src/bin/dhcp6/tests/Makefile.am @@ -1,6 +1,11 @@ PYCOVERAGE_RUN = @PYCOVERAGE_RUN@ PYTESTS = dhcp6_test.py -SHTESTS = dhcp6_reconfigure_test.sh +SHTESTS = +# The test of dynamic reconfiguration based on signals will work only +# if we are using file based configuration approach. +if CONFIG_BACKEND_JSON +SHTESTS += dhcp6_reconfigure_test.sh +endif EXTRA_DIST = $(PYTESTS) $(SHTESTS) # Explicitly specify paths to dynamic libraries required by loadable python @@ -49,7 +54,6 @@ TESTS_ENVIRONMENT = \ $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND) TESTS = -TEST_EXTENSIONS = .sh if HAVE_GTEST # Build shared libraries for testing. The libtool way to create a shared library # is to specify "-avoid-version -export-dynamic -module" in the library LDFLAGS diff --git a/src/bin/dhcp6/tests/dhcp6_reconfigure_test.sh b/src/bin/dhcp6/tests/dhcp6_reconfigure_test.sh index 5a733e4934..6d17130af0 100755 --- a/src/bin/dhcp6/tests/dhcp6_reconfigure_test.sh +++ b/src/bin/dhcp6/tests/dhcp6_reconfigure_test.sh @@ -12,6 +12,8 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. +# Test name +TEST_NAME="DynamicReconfiguration" # Name of the Kea executable. BIN="../b10-dhcp6" # Path to the temporary configuration file. @@ -54,13 +56,33 @@ cleanup() { printf "Shutting down Kea proccess having pid %d.\n" ${pid} kill -9 ${pid} done + rm -rf ${LOG_FILE} } cleanexit() { - cleanup + if [ $1 -eq 0 ]; then + cleanup + printf "PASSED ${TEST_NAME}\n\n" + else + printf "Log file dump:\n" + cat ${LOG_FILE} + cleanup + printf "FAILED ${TEST_NAME}\n\n" + fi exit $1 } +_GETRECONFIGS= +getreconfigs() { + # Grep log file for DHCP6_CONFIG_COMPLETE occurences. There should + # be one occurence per (re)configuration. + _GETRECONFIGS=`grep -o DHCP6_CONFIG_COMPLETE ${LOG_FILE} | wc -w` + # Remove whitespace + ${_GETRECONFIGS##*[! ]} +} + +printf "\nSTART TEST ${TEST_NAME}\n" + printf "Creating Kea configuration file: %s.\n" ${CFG_FILE} printf "%b" ${CONFIG} > ${CFG_FILE} @@ -84,6 +106,16 @@ if [ ${_GETPIDS2} -ne 1 ]; then cleanexit 1 fi +# Check in the log file, how many times server has been configured. It should +# be just once on startup. +getreconfigs +if [ ${_GETRECONFIGS} -ne 1 ]; then + printf "ERROR: server hasn't been configured.\n" + cleanexit 1 +else + printf "Server successfully configured\n" +fi + # Reconfigure the server with SIGUP. printf "Sending SIGUP to Kea process (pid=%s) to reconfigure the server.\n" ${_GETPIDS1} kill -1 ${_GETPIDS1} @@ -92,7 +124,18 @@ kill -1 ${_GETPIDS1} # didn't work. sleep 1 -# Make sure it is still operational. +# 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. +getreconfigs +if [ ${_GETRECONFIGS} -ne 2 ]; then + printf "ERROR: server hasn't been reconfigured.\n" + cleanexit 1 +else + printf "Server successfully configured\n" +fi + +# Make sure the server is still operational. getpids if [ ${_GETPIDS2} -ne 1 ]; then printf "ERROR: Kea process was killed when attempting reconfiguration.\n" diff --git a/src/lib/cc/data.cc b/src/lib/cc/data.cc index 26c5673bab..2a606576b8 100644 --- a/src/lib/cc/data.cc +++ b/src/lib/cc/data.cc @@ -736,8 +736,8 @@ Element::fromJSONFile(const std::string& file_name, if (!infile.is_open()) { const char* error = strerror(errno); - isc_throw(InvalidOperation, "Failed to read file " << file_name - << ",error:" << error); + isc_throw(InvalidOperation, "Failed to read file '" << file_name + << "', error:" << error); } return (fromJSON(infile, file_name, preproc)); diff --git a/src/lib/dhcpsrv/daemon.cc b/src/lib/dhcpsrv/daemon.cc index c37f923254..f3b361b91a 100644 --- a/src/lib/dhcpsrv/daemon.cc +++ b/src/lib/dhcpsrv/daemon.cc @@ -24,10 +24,14 @@ namespace isc { namespace dhcp { +// This is an initial config file location. +std::string Daemon::config_file_ = ""; + Daemon::Daemon() { } -void Daemon::init(const std::string&) { +void Daemon::init(const std::string& config_file) { + config_file_ = config_file; } void Daemon::cleanup() { diff --git a/src/lib/dhcpsrv/daemon.h b/src/lib/dhcpsrv/daemon.h index 7c14489044..52623f0ff3 100644 --- a/src/lib/dhcpsrv/daemon.h +++ b/src/lib/dhcpsrv/daemon.h @@ -34,6 +34,16 @@ namespace dhcp { /// Dhcpv6Srv) in tests, without going through the hassles of implemeting stub /// methods. /// +/// This class comprises a static object holding a location of the configuration +/// file. The object must be static because it is instantiated by the signal +/// handler functions, which are static by their nature. The signal handlers +/// are used to reconfigure a running server and they need access to the +/// configuration file location. They get this access by calling +/// @c Daemon::getConfigFile function. +/// +/// By default, the configuration file location is empty and its actual value +/// is assigned to the static object in @c Daemon::init function. +/// /// @note Only one instance of this class is instantiated as it encompasses /// the whole operation of the server. Nothing, however, enforces the /// singleton status of the object. @@ -44,13 +54,11 @@ public: /// /// Currently it does nothing. Daemon(); - + /// @brief Initializes the server. /// /// Depending on the configuration backend, it establishes msgq session, - /// or reads the - /// Creates session that will be used to receive commands and updated - /// configuration from cfgmgr (or indirectly from user via bindctl). + /// or reads the configuration file. /// /// Note: This function may throw to report enountered problems. It may /// also return false if the initialization was skipped. That may seem @@ -62,6 +70,8 @@ public: /// decide that it is not needed and will shut down. /// /// @note this method may throw + /// + /// @param config_file Config file name (may be empty if unused). virtual void init(const std::string& config_file); /// @brief Performs final deconfiguration. @@ -83,10 +93,20 @@ public: /// virtual destructor as well. virtual ~Daemon(); - /// Initializez logger + /// @brief Returns config file name. + static std::string getConfigFile() { + return (config_file_); + } + + /// Initializes logger /// - /// This method initializes logger. I + /// This method initializes logger. static void loggerInit(const char* log_name, bool verbose, bool stand_alone); + +private: + + /// @brief Config file name or empty if config file not used. + static std::string config_file_; }; }; // end of isc::dhcp namespace