}
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.
//
#include <cassert>
#include <iostream>
+#include <signal.h>
#include <string>
#include <vector>
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.
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.");
}
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);
}
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);
}
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() {
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
$(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
# 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.
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}
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}
# 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"
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));
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() {
/// 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.
///
/// 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
/// 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.
/// 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