Parcourir la source

[master] Merge branch 'trac3534'

Conflicts:
	src/lib/dhcpsrv/tests/daemon_unittest.cc
Marcin Siodelski il y a 10 ans
Parent
commit
4ecee3c0c9
45 fichiers modifiés avec 1773 ajouts et 581 suppressions
  1. 0 1
      configure.ac
  2. 34 5
      src/bin/d2/d_controller.cc
  3. 1 1
      src/bin/dhcp4/bundy_controller.cc
  4. 10 12
      src/bin/dhcp4/ctrl_dhcp4_srv.cc
  5. 5 6
      src/bin/dhcp4/json_config_parser.cc
  6. 18 5
      src/bin/dhcp4/kea_controller.cc
  7. 6 0
      src/bin/dhcp4/main.cc
  8. 4 2
      src/bin/dhcp4/tests/config_parser_unittest.cc
  9. 2 2
      src/bin/dhcp4/tests/dhcp4_test_utils.cc
  10. 8 13
      src/bin/dhcp6/ctrl_dhcp6_srv.cc
  11. 6 0
      src/bin/dhcp6/dhcp6.dox
  12. 0 6
      src/bin/dhcp6/dhcp6_srv.cc
  13. 5 6
      src/bin/dhcp6/json_config_parser.cc
  14. 18 2
      src/bin/dhcp6/kea_controller.cc
  15. 7 1
      src/bin/dhcp6/main.cc
  16. 6 6
      src/bin/dhcp6/tests/config_parser_unittest.cc
  17. 5 4
      src/bin/keactrl/Makefile.am
  18. 0 0
      src/bin/keactrl/kea.conf.pre
  19. 4 3
      src/lib/dhcpsrv/Makefile.am
  20. 21 13
      src/lib/dhcpsrv/cfg_iface.cc
  21. 39 29
      src/lib/dhcpsrv/cfg_iface.h
  22. 86 3
      src/lib/dhcpsrv/cfgmgr.cc
  23. 153 6
      src/lib/dhcpsrv/cfgmgr.h
  24. 0 64
      src/lib/dhcpsrv/configuration.cc
  25. 0 149
      src/lib/dhcpsrv/configuration.h
  26. 18 32
      src/lib/dhcpsrv/daemon.cc
  27. 4 12
      src/lib/dhcpsrv/daemon.h
  28. 8 21
      src/lib/dhcpsrv/dhcp_parsers.cc
  29. 6 17
      src/lib/dhcpsrv/dhcp_parsers.h
  30. 42 6
      src/lib/dhcpsrv/libdhcpsrv.dox
  31. 8 4
      src/lib/dhcpsrv/logging.cc
  32. 3 3
      src/lib/dhcpsrv/logging.h
  33. 145 0
      src/lib/dhcpsrv/logging_info.cc
  34. 131 0
      src/lib/dhcpsrv/logging_info.h
  35. 138 0
      src/lib/dhcpsrv/srv_config.cc
  36. 230 0
      src/lib/dhcpsrv/srv_config.h
  37. 2 1
      src/lib/dhcpsrv/tests/Makefile.am
  38. 94 43
      src/lib/dhcpsrv/tests/cfg_iface_unittest.cc
  39. 157 5
      src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
  40. 10 8
      src/lib/dhcpsrv/tests/daemon_unittest.cc
  41. 14 10
      src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
  42. 160 0
      src/lib/dhcpsrv/tests/logging_info_unittest.cc
  43. 36 36
      src/lib/dhcpsrv/tests/logging_unittest.cc
  44. 128 44
      src/lib/dhcpsrv/tests/configuration_unittest.cc
  45. 1 0
      tools/.gitignore

+ 0 - 1
configure.ac

@@ -1391,7 +1391,6 @@ AC_CONFIG_FILES([compatcheck/Makefile
                  src/bin/dhcp6/tests/test_data_files_config.h
                  src/bin/dhcp6/tests/test_data_files_config.h
                  src/bin/dhcp6/tests/test_libraries.h
                  src/bin/dhcp6/tests/test_libraries.h
                  src/bin/keactrl/Makefile
                  src/bin/keactrl/Makefile
-                 src/bin/keactrl/kea.conf
                  src/bin/keactrl/keactrl
                  src/bin/keactrl/keactrl
                  src/bin/keactrl/keactrl.conf
                  src/bin/keactrl/keactrl.conf
                  src/bin/keactrl/tests/Makefile
                  src/bin/keactrl/tests/Makefile

+ 34 - 5
src/bin/d2/d_controller.cc

@@ -18,7 +18,7 @@
 #include <d2/d_controller.h>
 #include <d2/d_controller.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 #include <log/logger_support.h>
 #include <log/logger_support.h>
-#include <dhcpsrv/configuration.h>
+#include <dhcpsrv/cfgmgr.h>
 
 
 #include <sstream>
 #include <sstream>
 #include <unistd.h>
 #include <unistd.h>
@@ -59,6 +59,16 @@ DControllerBase::launch(int argc, char* argv[], const bool test_mode) {
         throw; // rethrow it
         throw; // rethrow it
     }
     }
 
 
+    // It is important that we set a default logger name because this name
+    // will be used when the user doesn't provide the logging configuration
+    // in the Kea configuration file.
+    isc::dhcp::CfgMgr::instance().setDefaultLoggerName(bin_name_);
+
+    // Logger's default configuration depends on whether we are in the
+    // verbose mode or not. CfgMgr manages the logger configuration so
+    // the verbose mode is set for CfgMgr.
+    isc::dhcp::CfgMgr::instance().setVerbose(verbose_);
+
     // Do not initialize logger here if we are running unit tests. It would
     // Do not initialize logger here if we are running unit tests. It would
     // replace an instance of unit test specific logger.
     // replace an instance of unit test specific logger.
     if (!test_mode) {
     if (!test_mode) {
@@ -218,8 +228,13 @@ DControllerBase::initProcess() {
 
 
 isc::data::ConstElementPtr
 isc::data::ConstElementPtr
 DControllerBase::configFromFile() {
 DControllerBase::configFromFile() {
+    // Rollback any previous staging configuration. For D2, only a
+    // logger configuration is used here.
+    isc::dhcp::CfgMgr::instance().rollback();
+    // Will hold configuration.
     isc::data::ConstElementPtr module_config;
     isc::data::ConstElementPtr module_config;
-
+    // Will receive configuration result.
+    isc::data::ConstElementPtr answer;
     try {
     try {
         std::string config_file = getConfigFile();
         std::string config_file = getConfigFile();
         if (config_file.empty()) {
         if (config_file.empty()) {
@@ -236,11 +251,12 @@ DControllerBase::configFromFile() {
         // so we can log things during configuration process.
         // so we can log things during configuration process.
 
 
         // Temporary storage for logging configuration
         // Temporary storage for logging configuration
-        isc::dhcp::ConfigurationPtr storage(new isc::dhcp::Configuration());
+        isc::dhcp::SrvConfigPtr storage =
+            isc::dhcp::CfgMgr::instance().getStagingCfg();
 
 
         // Get 'Logging' element from the config and use it to set up
         // Get 'Logging' element from the config and use it to set up
         // logging. If there's no such element, we'll just pass NULL.
         // logging. If there's no such element, we'll just pass NULL.
-        Daemon::configureLogger(whole_config->get("Logging"), storage, verbose_);
+        Daemon::configureLogger(whole_config->get("Logging"), storage);
 
 
         // Extract derivation-specific portion of the configuration.
         // Extract derivation-specific portion of the configuration.
         module_config = whole_config->get(getAppName());
         module_config = whole_config->get(getAppName());
@@ -249,7 +265,20 @@ DControllerBase::configFromFile() {
                                 " does not include '" <<
                                 " does not include '" <<
                                  getAppName() << "' entry.");
                                  getAppName() << "' entry.");
         }
         }
+
+        answer = updateConfig(module_config);
+        int rcode = 0;
+        isc::config::parseAnswer(rcode, answer);
+        if (!rcode) {
+            // Configuration successful, so apply the logging configuration
+            // to log4cplus.
+            isc::dhcp::CfgMgr::instance().getStagingCfg()->applyLoggingCfg();
+            isc::dhcp::CfgMgr::instance().commit();
+        }
+
     } catch (const std::exception& ex) {
     } catch (const std::exception& ex) {
+        // Rollback logging configuration.
+        isc::dhcp::CfgMgr::instance().rollback();
         // build an error result
         // build an error result
         isc::data::ConstElementPtr error =
         isc::data::ConstElementPtr error =
             isc::config::createAnswer(1,
             isc::config::createAnswer(1,
@@ -257,7 +286,7 @@ DControllerBase::configFromFile() {
         return (error);
         return (error);
     }
     }
 
 
-    return (updateConfig(module_config));
+    return (answer);
 }
 }
 
 
 
 

+ 1 - 1
src/bin/dhcp4/bundy_controller.cc

@@ -177,7 +177,7 @@ void ControlledDhcpv4Srv::init(const std::string& config_file) {
 
 
         // Configuration may disable or enable interfaces so we have to
         // Configuration may disable or enable interfaces so we have to
         // reopen sockets according to new configuration.
         // reopen sockets according to new configuration.
-        CfgMgr::instance().getConfiguration()->cfg_iface_
+        CfgMgr::instance().getCurrentCfg()->cfg_iface_
             .openSockets(getPort(), useBroadcast());
             .openSockets(getPort(), useBroadcast());
 
 
     } catch (const std::exception& ex) {
     } catch (const std::exception& ex) {

+ 10 - 12
src/bin/dhcp4/ctrl_dhcp4_srv.cc

@@ -145,18 +145,16 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
     }
     }
 
 
     // Configuration may change active interfaces. Therefore, we have to reopen
     // Configuration may change active interfaces. Therefore, we have to reopen
-    // sockets according to new configuration. This operation is not exception
-    // safe and we really don't want to emit exceptions to whoever called this
-    // method. Instead, catch an exception and create appropriate answer.
-    try {
-        CfgMgr::instance().getConfiguration()->cfg_iface_
-            .openSockets(srv->getPort(), getInstance()->useBroadcast());
+    // sockets according to new configuration. It is possible that this
+    // operation will fail for some interfaces but the openSockets function
+    // guards against exceptions and invokes a callback function to
+    // log warnings. Since we allow that this fails for some interfaces there
+    // is no need to rollback configuration if socket fails to open on any
+    // of the interfaces.
+    CfgMgr::instance().getStagingCfg()->
+        getCfgIface().openSockets(AF_INET, srv->getPort(),
+                                  getInstance()->useBroadcast());
 
 
-    } catch (std::exception& ex) {
-        err << "failed to open sockets after server reconfiguration: "
-            << ex.what();
-        answer = isc::config::createAnswer(1, err.str());
-    }
     return (answer);
     return (answer);
 }
 }
 
 
@@ -176,7 +174,7 @@ void ControlledDhcpv4Srv::shutdown() {
 
 
 ControlledDhcpv4Srv::~ControlledDhcpv4Srv() {
 ControlledDhcpv4Srv::~ControlledDhcpv4Srv() {
     cleanup();
     cleanup();
-    
+
     server_ = NULL; // forget this instance. Noone should call any handlers at
     server_ = NULL; // forget this instance. Noone should call any handlers at
                     // this stage.
                     // this stage.
 }
 }

+ 5 - 6
src/bin/dhcp4/json_config_parser.cc

@@ -443,7 +443,7 @@ namespace dhcp {
         parser = new Uint32Parser(config_id,
         parser = new Uint32Parser(config_id,
                                  globalContext()->uint32_values_);
                                  globalContext()->uint32_values_);
     } else if (config_id.compare("interfaces") == 0) {
     } else if (config_id.compare("interfaces") == 0) {
-        parser = new InterfaceListConfigParser(config_id);
+        parser = new InterfaceListConfigParser(config_id, globalContext());
     } else if (config_id.compare("subnet4") == 0) {
     } else if (config_id.compare("subnet4") == 0) {
         parser = new Subnets4ListConfigParser(config_id);
         parser = new Subnets4ListConfigParser(config_id);
     } else if (config_id.compare("option-data") == 0) {
     } else if (config_id.compare("option-data") == 0) {
@@ -619,9 +619,8 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
                 subnet_parser->commit();
                 subnet_parser->commit();
             }
             }
 
 
-            if (iface_parser) {
-                iface_parser->commit();
-            }
+            // No need to commit interface names as this is handled by the
+            // CfgMgr::commit() function.
 
 
             // Apply global options
             // Apply global options
             commitGlobalOptions();
             commitGlobalOptions();
@@ -653,8 +652,8 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
     }
     }
 
 
     LOG_INFO(dhcp4_logger, DHCP4_CONFIG_COMPLETE)
     LOG_INFO(dhcp4_logger, DHCP4_CONFIG_COMPLETE)
-        .arg(CfgMgr::instance().getConfiguration()->
-             getConfigSummary(Configuration::CFGSEL_ALL4));
+        .arg(CfgMgr::instance().getCurrentCfg()->
+             getConfigSummary(SrvConfig::CFGSEL_ALL4));
 
 
     // Everything was fine. Configuration is successful.
     // Everything was fine. Configuration is successful.
     answer = isc::config::createAnswer(0, "Configuration successful.");
     answer = isc::config::createAnswer(0, "Configuration successful.");

+ 18 - 5
src/bin/dhcp4/kea_controller.cc

@@ -41,6 +41,11 @@ void configure(const std::string& file_name) {
     // This is a configuration backend implementation that reads the
     // This is a configuration backend implementation that reads the
     // configuration from a JSON file.
     // configuration from a JSON file.
 
 
+    // We are starting the configuration process so we should remove any
+    // staging configuration that has been created during previous
+    // configuration attempts.
+    CfgMgr::instance().rollback();
+
     isc::data::ConstElementPtr json;
     isc::data::ConstElementPtr json;
     isc::data::ConstElementPtr dhcp4;
     isc::data::ConstElementPtr dhcp4;
     isc::data::ConstElementPtr logger;
     isc::data::ConstElementPtr logger;
@@ -60,14 +65,10 @@ void configure(const std::string& file_name) {
             isc_throw(isc::BadValue, "no configuration found");
             isc_throw(isc::BadValue, "no configuration found");
         }
         }
 
 
-        // Let's configure logging before applying the configuration,
-        // so we can log things during configuration process.
-
         // If there's no logging element, we'll just pass NULL pointer,
         // If there's no logging element, we'll just pass NULL pointer,
         // which will be handled by configureLogger().
         // which will be handled by configureLogger().
         Daemon::configureLogger(json->get("Logging"),
         Daemon::configureLogger(json->get("Logging"),
-                                CfgMgr::instance().getConfiguration(),
-                                ControlledDhcpv4Srv::getInstance()->getVerbose());
+                                CfgMgr::instance().getStagingCfg());
 
 
         // Get Dhcp4 component from the config
         // Get Dhcp4 component from the config
         dhcp4 = json->get("Dhcp4");
         dhcp4 = json->get("Dhcp4");
@@ -97,7 +98,19 @@ void configure(const std::string& file_name) {
             isc_throw(isc::BadValue, reason);
             isc_throw(isc::BadValue, reason);
         }
         }
 
 
+        // If configuration was parsed successfully, apply the new logger
+        // configuration to log4cplus. It is done before commit in case
+        // something goes wrong.
+        CfgMgr::instance().getStagingCfg()->applyLoggingCfg();
+
+        // Use new configuration.
+        CfgMgr::instance().commit();
+
     }  catch (const std::exception& ex) {
     }  catch (const std::exception& ex) {
+        // If configuration failed at any stage, we drop the staging
+        // configuration and continue to use the previous one.
+        CfgMgr::instance().rollback();
+
         LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL)
         LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL)
             .arg(file_name).arg(ex.what());
             .arg(file_name).arg(ex.what());
         isc_throw(isc::BadValue, "configuration error using file '"
         isc_throw(isc::BadValue, "configuration error using file '"

+ 6 - 0
src/bin/dhcp4/main.cc

@@ -16,6 +16,7 @@
 
 
 #include <dhcp4/ctrl_dhcp4_srv.h>
 #include <dhcp4/ctrl_dhcp4_srv.h>
 #include <dhcp4/dhcp4_log.h>
 #include <dhcp4/dhcp4_log.h>
+#include <dhcpsrv/cfgmgr.h>
 #include <log/logger_support.h>
 #include <log/logger_support.h>
 #include <log/logger_manager.h>
 #include <log/logger_manager.h>
 
 
@@ -122,6 +123,11 @@ main(int argc, char* argv[]) {
     int ret = EXIT_SUCCESS;
     int ret = EXIT_SUCCESS;
 
 
     try {
     try {
+        // It is important that we set a default logger name because this name
+        // will be used when the user doesn't provide the logging configuration
+        // in the Kea configuration file.
+        CfgMgr::instance().setDefaultLoggerName(DHCP4_LOGGER_NAME);
+
         // Initialize logging.  If verbose, we'll use maximum verbosity.
         // Initialize logging.  If verbose, we'll use maximum verbosity.
         Daemon::loggerInit(DHCP4_LOGGER_NAME, verbose_mode);
         Daemon::loggerInit(DHCP4_LOGGER_NAME, verbose_mode);
         LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_START_INFO)
         LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_START_INFO)

+ 4 - 2
src/bin/dhcp4/tests/config_parser_unittest.cc

@@ -2917,7 +2917,8 @@ TEST_F(Dhcp4ParserTest, selectedInterfaces) {
     ASSERT_TRUE(status);
     ASSERT_TRUE(status);
     checkResult(status, 0);
     checkResult(status, 0);
 
 
-    CfgMgr::instance().getConfiguration()->cfg_iface_.openSockets(10000);
+    CfgMgr::instance().getStagingCfg()->
+        getCfgIface().openSockets(AF_INET, 10000);
 
 
     // eth0 and eth1 were explicitly selected. eth2 was not.
     // eth0 and eth1 were explicitly selected. eth2 was not.
     EXPECT_TRUE(test_config.socketOpen("eth0", AF_INET));
     EXPECT_TRUE(test_config.socketOpen("eth0", AF_INET));
@@ -2952,7 +2953,8 @@ TEST_F(Dhcp4ParserTest, allInterfaces) {
     ASSERT_TRUE(status);
     ASSERT_TRUE(status);
     checkResult(status, 0);
     checkResult(status, 0);
 
 
-    CfgMgr::instance().getConfiguration()->cfg_iface_.openSockets(10000);
+    CfgMgr::instance().getStagingCfg()->
+        getCfgIface().openSockets(AF_INET, 10000);
 
 
     // All interfaces should be now active.
     // All interfaces should be now active.
     ASSERT_TRUE(test_config.socketOpen("eth0", AF_INET));
     ASSERT_TRUE(test_config.socketOpen("eth0", AF_INET));

+ 2 - 2
src/bin/dhcp4/tests/dhcp4_test_utils.cc

@@ -45,7 +45,7 @@ Dhcpv4SrvTest::Dhcpv4SrvTest()
     pool_ = Pool4Ptr(new Pool4(IOAddress("192.0.2.100"), IOAddress("192.0.2.110")));
     pool_ = Pool4Ptr(new Pool4(IOAddress("192.0.2.100"), IOAddress("192.0.2.110")));
     subnet_->addPool(pool_);
     subnet_->addPool(pool_);
 
 
-    CfgMgr::instance().getConfiguration()->cfg_iface_.reset();
+    CfgMgr::instance().clear();
     CfgMgr::instance().deleteSubnets4();
     CfgMgr::instance().deleteSubnets4();
     CfgMgr::instance().addSubnet4(subnet_);
     CfgMgr::instance().addSubnet4(subnet_);
 
 
@@ -58,7 +58,7 @@ Dhcpv4SrvTest::Dhcpv4SrvTest()
 Dhcpv4SrvTest::~Dhcpv4SrvTest() {
 Dhcpv4SrvTest::~Dhcpv4SrvTest() {
 
 
     // Make sure that we revert to default value
     // Make sure that we revert to default value
-    CfgMgr::instance().getConfiguration()->cfg_iface_.reset();
+    CfgMgr::instance().clear();
     CfgMgr::instance().echoClientId(true);
     CfgMgr::instance().echoClientId(true);
 }
 }
 
 

+ 8 - 13
src/bin/dhcp6/ctrl_dhcp6_srv.cc

@@ -140,19 +140,14 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
     }
     }
 
 
     // Configuration may change active interfaces. Therefore, we have to reopen
     // Configuration may change active interfaces. Therefore, we have to reopen
-    // sockets according to new configuration. This operation is not exception
-    // safe and we really don't want to emit exceptions to the callback caller.
-    // Instead, catch an exception and create appropriate answer.
-    try {
-        CfgMgr::instance().getConfiguration()->cfg_iface_
-            .openSockets(srv->getPort());
-
-    } catch (const std::exception& ex) {
-        std::ostringstream err;
-        err << "failed to open sockets after server reconfiguration: "
-            << ex.what();
-        answer = isc::config::createAnswer(1, err.str());
-    }
+    // sockets according to new configuration. It is possible that this
+    // operation will fail for some interfaces but the openSockets function
+    // guards against exceptions and invokes a callback function to
+    // log warnings. Since we allow that this fails for some interfaces there
+    // is no need to rollback configuration if socket fails to open on any
+    // of the interfaces.
+    CfgMgr::instance().getStagingCfg()->
+        getCfgIface().openSockets(AF_INET6, srv->getPort());
 
 
     return (answer);
     return (answer);
 }
 }

+ 6 - 0
src/bin/dhcp6/dhcp6.dox

@@ -55,6 +55,12 @@ following section applies only to the Bundy backend.
  build, the configuration is then applied ("committed") and commit() method is
  build, the configuration is then applied ("committed") and commit() method is
  called.
  called.
 
 
+ \note With the implementation of the Kea ticket #3534 we're moving away from
+ the concept of commit being called for individual parsers. When this ticket
+ and a couple of followup tickets are implemented, the commit will be a
+ single operation executed for the whole configuration received from many
+ parsers.
+
  All parsers are defined in src/bin/dhcp6/config_parser.cc file. Some of them
  All parsers are defined in src/bin/dhcp6/config_parser.cc file. Some of them
  are generic (e.g. Uint32Parser that is able to handle any
  are generic (e.g. Uint32Parser that is able to handle any
  unsigned 32 bit integer), but some are very specialized (e.g.
  unsigned 32 bit integer), but some are very specialized (e.g.

+ 0 - 6
src/bin/dhcp6/dhcp6_srv.cc

@@ -150,12 +150,6 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port)
         // Instantiate allocation engine
         // Instantiate allocation engine
         alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));
         alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));
 
 
-        // We have to point out to the CfgMgr that the we are in the IPv6
-        // domain, so as the IPv6 sockets are opened rather than IPv4 sockets
-        // which are the default.
-        CfgMgr::instance().getConfiguration()
-            ->cfg_iface_.setFamily(CfgIface::V6);
-
         /// @todo call loadLibraries() when handling configuration changes
         /// @todo call loadLibraries() when handling configuration changes
 
 
     } catch (const std::exception &e) {
     } catch (const std::exception &e) {

+ 5 - 6
src/bin/dhcp6/json_config_parser.cc

@@ -662,7 +662,7 @@ namespace dhcp {
         parser = new Uint32Parser(config_id,
         parser = new Uint32Parser(config_id,
                                  globalContext()->uint32_values_);
                                  globalContext()->uint32_values_);
     } else if (config_id.compare("interfaces") == 0) {
     } else if (config_id.compare("interfaces") == 0) {
-        parser = new InterfaceListConfigParser(config_id);
+        parser = new InterfaceListConfigParser(config_id, globalContext());
     } else if (config_id.compare("subnet6") == 0) {
     } else if (config_id.compare("subnet6") == 0) {
         parser = new Subnets6ListConfigParser(config_id);
         parser = new Subnets6ListConfigParser(config_id);
     } else if (config_id.compare("option-data") == 0) {
     } else if (config_id.compare("option-data") == 0) {
@@ -821,9 +821,8 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
                 subnet_parser->commit();
                 subnet_parser->commit();
             }
             }
 
 
-            if (iface_parser) {
-                iface_parser->commit();
-            }
+            // No need to commit interface names as this is handled by the
+            // CfgMgr::commit() function.
 
 
             // This occurs last as if it succeeds, there is no easy way to
             // This occurs last as if it succeeds, there is no easy way to
             // revert it.  As a result, the failure to commit a subsequent
             // revert it.  As a result, the failure to commit a subsequent
@@ -854,8 +853,8 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
     }
     }
 
 
     LOG_INFO(dhcp6_logger, DHCP6_CONFIG_COMPLETE)
     LOG_INFO(dhcp6_logger, DHCP6_CONFIG_COMPLETE)
-        .arg(CfgMgr::instance().getConfiguration()->
-             getConfigSummary(Configuration::CFGSEL_ALL6));
+        .arg(CfgMgr::instance().getCurrentCfg()->
+             getConfigSummary(SrvConfig::CFGSEL_ALL6));
 
 
     // Everything was fine. Configuration is successful.
     // Everything was fine. Configuration is successful.
     answer = isc::config::createAnswer(0, "Configuration successful.");
     answer = isc::config::createAnswer(0, "Configuration successful.");

+ 18 - 2
src/bin/dhcp6/kea_controller.cc

@@ -45,6 +45,11 @@ void configure(const std::string& file_name) {
     // This is a configuration backend implementation that reads the
     // This is a configuration backend implementation that reads the
     // configuration from a JSON file.
     // configuration from a JSON file.
 
 
+    // We are starting the configuration process so we should remove any
+    // staging configuration that has been created during previous
+    // configuration attempts.
+    CfgMgr::instance().rollback();
+
     isc::data::ConstElementPtr json;
     isc::data::ConstElementPtr json;
     isc::data::ConstElementPtr dhcp6;
     isc::data::ConstElementPtr dhcp6;
     isc::data::ConstElementPtr logger;
     isc::data::ConstElementPtr logger;
@@ -69,8 +74,7 @@ void configure(const std::string& file_name) {
         // If there's no logging element, we'll just pass NULL pointer,
         // If there's no logging element, we'll just pass NULL pointer,
         // which will be handled by configureLogger().
         // which will be handled by configureLogger().
         Daemon::configureLogger(json->get("Logging"),
         Daemon::configureLogger(json->get("Logging"),
-                                CfgMgr::instance().getConfiguration(),
-                                ControlledDhcpv6Srv::getInstance()->getVerbose());
+                                CfgMgr::instance().getStagingCfg());
 
 
         // Get Dhcp6 component from the config
         // Get Dhcp6 component from the config
         dhcp6 = json->get("Dhcp6");
         dhcp6 = json->get("Dhcp6");
@@ -101,7 +105,19 @@ void configure(const std::string& file_name) {
             isc_throw(isc::BadValue, reason);
             isc_throw(isc::BadValue, reason);
         }
         }
 
 
+        // If configuration was parsed successfully, apply the new logger
+        // configuration to log4cplus. It is done before commit in case
+        // something goes wrong.
+        CfgMgr::instance().getStagingCfg()->applyLoggingCfg();
+
+        // Use new configuration.
+        CfgMgr::instance().commit();
+
     }  catch (const std::exception& ex) {
     }  catch (const std::exception& ex) {
+        // If configuration failed at any stage, we drop the staging
+        // configuration and continue to use the previous one.
+        CfgMgr::instance().rollback();
+
         LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL)
         LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL)
             .arg(file_name).arg(ex.what());
             .arg(file_name).arg(ex.what());
         isc_throw(isc::BadValue, "configuration error using file '"
         isc_throw(isc::BadValue, "configuration error using file '"

+ 7 - 1
src/bin/dhcp6/main.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2014 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -16,6 +16,7 @@
 
 
 #include <dhcp6/ctrl_dhcp6_srv.h>
 #include <dhcp6/ctrl_dhcp6_srv.h>
 #include <dhcp6/dhcp6_log.h>
 #include <dhcp6/dhcp6_log.h>
+#include <dhcpsrv/cfgmgr.h>
 #include <log/logger_support.h>
 #include <log/logger_support.h>
 #include <log/logger_manager.h>
 #include <log/logger_manager.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
@@ -122,6 +123,11 @@ main(int argc, char* argv[]) {
 
 
     int ret = EXIT_SUCCESS;
     int ret = EXIT_SUCCESS;
     try {
     try {
+        // It is important that we set a default logger name because this name
+        // will be used when the user doesn't provide the logging configuration
+        // in the Kea configuration file.
+        CfgMgr::instance().setDefaultLoggerName(DHCP6_LOGGER_NAME);
+
         // Initialize logging.  If verbose, we'll use maximum verbosity.
         // Initialize logging.  If verbose, we'll use maximum verbosity.
         Daemon::loggerInit(DHCP6_LOGGER_NAME, verbose_mode);
         Daemon::loggerInit(DHCP6_LOGGER_NAME, verbose_mode);
 
 

+ 6 - 6
src/bin/dhcp6/tests/config_parser_unittest.cc

@@ -359,9 +359,7 @@ public:
         // properly test interface configuration we disable listening on
         // properly test interface configuration we disable listening on
         // all interfaces before each test and later check that this setting
         // all interfaces before each test and later check that this setting
         // has been overriden by the configuration used in the test.
         // has been overriden by the configuration used in the test.
-        CfgMgr::instance().getConfiguration()->cfg_iface_.reset();
-        CfgMgr::instance().getConfiguration()->
-            cfg_iface_.setFamily(CfgIface::V6);
+        CfgMgr::instance().clear();
         // Create fresh context.
         // Create fresh context.
         globalContext()->copyContext(ParserContext(Option::V6));
         globalContext()->copyContext(ParserContext(Option::V6));
     }
     }
@@ -3057,7 +3055,8 @@ TEST_F(Dhcp6ParserTest, selectedInterfaces) {
     // as the pool does not belong to that subnet
     // as the pool does not belong to that subnet
     checkResult(status, 0);
     checkResult(status, 0);
 
 
-    CfgMgr::instance().getConfiguration()->cfg_iface_.openSockets(10000);
+    CfgMgr::instance().getStagingCfg()->
+        getCfgIface().openSockets(AF_INET6, 10000);
 
 
     // eth0 and eth1 were explicitly selected. eth2 was not.
     // eth0 and eth1 were explicitly selected. eth2 was not.
     EXPECT_TRUE(test_config.socketOpen("eth0", AF_INET6));
     EXPECT_TRUE(test_config.socketOpen("eth0", AF_INET6));
@@ -3075,7 +3074,7 @@ TEST_F(Dhcp6ParserTest, allInterfaces) {
     ConstElementPtr status;
     ConstElementPtr status;
 
 
     // This configuration specifies two interfaces on which server should listen
     // This configuration specifies two interfaces on which server should listen
-    // bu also includes keyword 'all'. This keyword switches server into the
+    // but also includes '*'. This keyword switches server into the
     // mode when it listens on all interfaces regardless of what interface names
     // mode when it listens on all interfaces regardless of what interface names
     // were specified in the "interfaces" parameter.
     // were specified in the "interfaces" parameter.
     string config = "{ \"interfaces\": [ \"eth0\", \"eth1\", \"*\" ],"
     string config = "{ \"interfaces\": [ \"eth0\", \"eth1\", \"*\" ],"
@@ -3090,7 +3089,8 @@ TEST_F(Dhcp6ParserTest, allInterfaces) {
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     checkResult(status, 0);
     checkResult(status, 0);
 
 
-    CfgMgr::instance().getConfiguration()->cfg_iface_.openSockets(10000);
+    CfgMgr::instance().getStagingCfg()->
+        getCfgIface().openSockets(AF_INET6, 10000);
 
 
     // All interfaces should be now active.
     // All interfaces should be now active.
     EXPECT_TRUE(test_config.socketOpen("eth0", AF_INET6));
     EXPECT_TRUE(test_config.socketOpen("eth0", AF_INET6));

+ 5 - 4
src/bin/keactrl/Makefile.am

@@ -8,8 +8,9 @@ sbin_SCRIPTS  = keactrl
 CONFIGFILES = keactrl.conf kea.conf
 CONFIGFILES = keactrl.conf kea.conf
 
 
 man_MANS = keactrl.8
 man_MANS = keactrl.8
-DISTCLEANFILES = keactrl $(CONFIGFILES) $(man_MANS)
-EXTRA_DIST = keactrl.in keactrl.conf.in kea.conf.in $(man_MANS) keactrl.xml
+DISTCLEANFILES = keactrl keactrl.conf $(man_MANS)
+CLEANFILES = kea.conf
+EXTRA_DIST = keactrl.in keactrl.conf.in kea.conf.pre $(man_MANS) keactrl.xml
 
 
 # kea.conf is not really a source used for building other targets, but we need
 # kea.conf is not really a source used for building other targets, but we need
 # this file to be generated before make install is called.
 # this file to be generated before make install is called.
@@ -28,8 +29,8 @@ $(man_MANS):
 
 
 endif
 endif
 
 
-kea.conf: kea.conf.in
-	$(top_srcdir)/tools/path_replacer.sh $< $@
+kea.conf: kea.conf.pre
+	$(top_srcdir)/tools/path_replacer.sh $(top_srcdir)/src/bin/keactrl/kea.conf.pre $@
 
 
 if INSTALL_CONFIGURATIONS
 if INSTALL_CONFIGURATIONS
 
 

src/bin/keactrl/kea.conf.in → src/bin/keactrl/kea.conf.pre


+ 4 - 3
src/lib/dhcpsrv/Makefile.am

@@ -45,6 +45,8 @@ libkea_dhcpsrv_la_SOURCES  =
 libkea_dhcpsrv_la_SOURCES += addr_utilities.cc addr_utilities.h
 libkea_dhcpsrv_la_SOURCES += addr_utilities.cc addr_utilities.h
 libkea_dhcpsrv_la_SOURCES += alloc_engine.cc alloc_engine.h
 libkea_dhcpsrv_la_SOURCES += alloc_engine.cc alloc_engine.h
 libkea_dhcpsrv_la_SOURCES += callout_handle_store.h
 libkea_dhcpsrv_la_SOURCES += callout_handle_store.h
+libkea_dhcpsrv_la_SOURCES += cfg_iface.cc cfg_iface.h
+libkea_dhcpsrv_la_SOURCES += cfgmgr.cc cfgmgr.h
 libkea_dhcpsrv_la_SOURCES += csv_lease_file4.cc csv_lease_file4.h
 libkea_dhcpsrv_la_SOURCES += csv_lease_file4.cc csv_lease_file4.h
 libkea_dhcpsrv_la_SOURCES += csv_lease_file6.cc csv_lease_file6.h
 libkea_dhcpsrv_la_SOURCES += csv_lease_file6.cc csv_lease_file6.h
 libkea_dhcpsrv_la_SOURCES += d2_client_cfg.cc d2_client_cfg.h
 libkea_dhcpsrv_la_SOURCES += d2_client_cfg.cc d2_client_cfg.h
@@ -52,16 +54,14 @@ libkea_dhcpsrv_la_SOURCES += d2_client_mgr.cc d2_client_mgr.h
 libkea_dhcpsrv_la_SOURCES += daemon.cc daemon.h
 libkea_dhcpsrv_la_SOURCES += daemon.cc daemon.h
 libkea_dhcpsrv_la_SOURCES += dbaccess_parser.cc dbaccess_parser.h
 libkea_dhcpsrv_la_SOURCES += dbaccess_parser.cc dbaccess_parser.h
 libkea_dhcpsrv_la_SOURCES += dhcpsrv_log.cc dhcpsrv_log.h
 libkea_dhcpsrv_la_SOURCES += dhcpsrv_log.cc dhcpsrv_log.h
-libkea_dhcpsrv_la_SOURCES += cfgmgr.cc cfgmgr.h
 libkea_dhcpsrv_la_SOURCES += dhcp_config_parser.h
 libkea_dhcpsrv_la_SOURCES += dhcp_config_parser.h
 libkea_dhcpsrv_la_SOURCES += dhcp_parsers.cc dhcp_parsers.h
 libkea_dhcpsrv_la_SOURCES += dhcp_parsers.cc dhcp_parsers.h
-libkea_dhcpsrv_la_SOURCES += cfg_iface.cc cfg_iface.h
 libkea_dhcpsrv_la_SOURCES += key_from_key.h
 libkea_dhcpsrv_la_SOURCES += key_from_key.h
 libkea_dhcpsrv_la_SOURCES += lease.cc lease.h
 libkea_dhcpsrv_la_SOURCES += lease.cc lease.h
 libkea_dhcpsrv_la_SOURCES += lease_mgr.cc lease_mgr.h
 libkea_dhcpsrv_la_SOURCES += lease_mgr.cc lease_mgr.h
 libkea_dhcpsrv_la_SOURCES += lease_mgr_factory.cc lease_mgr_factory.h
 libkea_dhcpsrv_la_SOURCES += lease_mgr_factory.cc lease_mgr_factory.h
 libkea_dhcpsrv_la_SOURCES += logging.cc logging.h
 libkea_dhcpsrv_la_SOURCES += logging.cc logging.h
-libkea_dhcpsrv_la_SOURCES += configuration.h configuration.cc
+libkea_dhcpsrv_la_SOURCES += logging_info.cc logging_info.h
 libkea_dhcpsrv_la_SOURCES += memfile_lease_mgr.cc memfile_lease_mgr.h
 libkea_dhcpsrv_la_SOURCES += memfile_lease_mgr.cc memfile_lease_mgr.h
 
 
 if HAVE_MYSQL
 if HAVE_MYSQL
@@ -72,6 +72,7 @@ libkea_dhcpsrv_la_SOURCES += pgsql_lease_mgr.cc pgsql_lease_mgr.h
 endif
 endif
 libkea_dhcpsrv_la_SOURCES += option_space_container.h
 libkea_dhcpsrv_la_SOURCES += option_space_container.h
 libkea_dhcpsrv_la_SOURCES += pool.cc pool.h
 libkea_dhcpsrv_la_SOURCES += pool.cc pool.h
+libkea_dhcpsrv_la_SOURCES += srv_config.cc srv_config.h
 libkea_dhcpsrv_la_SOURCES += subnet.cc subnet.h
 libkea_dhcpsrv_la_SOURCES += subnet.cc subnet.h
 libkea_dhcpsrv_la_SOURCES += triplet.h
 libkea_dhcpsrv_la_SOURCES += triplet.h
 libkea_dhcpsrv_la_SOURCES += utils.h
 libkea_dhcpsrv_la_SOURCES += utils.h

+ 21 - 13
src/lib/dhcpsrv/cfg_iface.cc

@@ -25,24 +25,31 @@ namespace dhcp {
 
 
 const char* CfgIface::ALL_IFACES_KEYWORD = "*";
 const char* CfgIface::ALL_IFACES_KEYWORD = "*";
 
 
-CfgIface::CfgIface(Family family)
-    : family_(family),
-      wildcard_used_(false) {
+CfgIface::CfgIface()
+    : wildcard_used_(false) {
 }
 }
 
 
 void
 void
-CfgIface::closeSockets() {
+CfgIface::closeSockets() const {
     IfaceMgr::instance().closeSockets();
     IfaceMgr::instance().closeSockets();
 }
 }
 
 
+bool
+CfgIface::equals(const CfgIface& other) const {
+    return (iface_set_ == other.iface_set_ &&
+            unicast_map_ == other.unicast_map_ &&
+            wildcard_used_ == other.wildcard_used_);
+}
+
 void
 void
-CfgIface::openSockets(const uint16_t port, const bool use_bcast) {
+CfgIface::openSockets(const uint16_t family, const uint16_t port,
+                      const bool use_bcast) const {
     // If wildcard interface '*' was not specified, set all interfaces to
     // If wildcard interface '*' was not specified, set all interfaces to
     // inactive state. We will later enable them selectively using the
     // inactive state. We will later enable them selectively using the
     // interface names specified by the user. If wildcard interface was
     // interface names specified by the user. If wildcard interface was
     // specified, mark all interfaces active. In all cases, mark loopback
     // specified, mark all interfaces active. In all cases, mark loopback
     // inactive.
     // inactive.
-    setState(!wildcard_used_, true);
+    setState(family, !wildcard_used_, true);
     // Remove selection of unicast addresses from all interfaces.
     // Remove selection of unicast addresses from all interfaces.
     IfaceMgr::instance().clearUnicasts();
     IfaceMgr::instance().clearUnicasts();
     // If there is no wildcard interface specified, we will have to iterate
     // If there is no wildcard interface specified, we will have to iterate
@@ -61,7 +68,7 @@ CfgIface::openSockets(const uint16_t port, const bool use_bcast) {
                           << *iface_name << "' as this interface doesn't"
                           << *iface_name << "' as this interface doesn't"
                           " exist");
                           " exist");
 
 
-            } else if (getFamily() == V4) {
+            } else if (family == AF_INET) {
                 iface->inactive4_ = false;
                 iface->inactive4_ = false;
 
 
             } else {
             } else {
@@ -71,7 +78,7 @@ CfgIface::openSockets(const uint16_t port, const bool use_bcast) {
     }
     }
 
 
     // Select unicast sockets. It works only for V6. Ignore for V4.
     // Select unicast sockets. It works only for V6. Ignore for V4.
-    if (getFamily() == V6) {
+    if (family == AF_INET6) {
         for (UnicastMap::const_iterator unicast = unicast_map_.begin();
         for (UnicastMap::const_iterator unicast = unicast_map_.begin();
              unicast != unicast_map_.end(); ++unicast) {
              unicast != unicast_map_.end(); ++unicast) {
             Iface* iface = IfaceMgr::instance().getIface(unicast->first);
             Iface* iface = IfaceMgr::instance().getIface(unicast->first);
@@ -95,7 +102,7 @@ CfgIface::openSockets(const uint16_t port, const bool use_bcast) {
     IfaceMgrErrorMsgCallback error_callback =
     IfaceMgrErrorMsgCallback error_callback =
         boost::bind(&CfgIface::socketOpenErrorHandler, _1);
         boost::bind(&CfgIface::socketOpenErrorHandler, _1);
     bool sopen;
     bool sopen;
-    if (getFamily() == V4) {
+    if (family == AF_INET) {
         sopen = IfaceMgr::instance().openSockets4(port, use_bcast,
         sopen = IfaceMgr::instance().openSockets4(port, use_bcast,
                                                   error_callback);
                                                   error_callback);
     } else {
     } else {
@@ -117,12 +124,13 @@ CfgIface::reset() {
 }
 }
 
 
 void
 void
-CfgIface::setState(const bool inactive, const bool loopback_inactive) {
+CfgIface::setState(const uint16_t family, const bool inactive,
+                   const bool loopback_inactive) const {
     IfaceMgr::IfaceCollection ifaces = IfaceMgr::instance().getIfaces();
     IfaceMgr::IfaceCollection ifaces = IfaceMgr::instance().getIfaces();
     for (IfaceMgr::IfaceCollection::iterator iface = ifaces.begin();
     for (IfaceMgr::IfaceCollection::iterator iface = ifaces.begin();
          iface != ifaces.end(); ++iface) {
          iface != ifaces.end(); ++iface) {
         Iface* iface_ptr = IfaceMgr::instance().getIface(iface->getName());
         Iface* iface_ptr = IfaceMgr::instance().getIface(iface->getName());
-        if (getFamily() == V4) {
+        if (family == AF_INET) {
             iface_ptr->inactive4_ = iface_ptr->flag_loopback_ ?
             iface_ptr->inactive4_ = iface_ptr->flag_loopback_ ?
                 loopback_inactive : inactive;
                 loopback_inactive : inactive;
         } else {
         } else {
@@ -138,7 +146,7 @@ CfgIface::socketOpenErrorHandler(const std::string& errmsg) {
 }
 }
 
 
 void
 void
-CfgIface::use(const std::string& iface_name) {
+CfgIface::use(const uint16_t family, const std::string& iface_name) {
     // The interface name specified may have two formats, e.g.:
     // The interface name specified may have two formats, e.g.:
     // - eth0
     // - eth0
     // - eth0/2001:db8:1::1.
     // - eth0/2001:db8:1::1.
@@ -184,7 +192,7 @@ CfgIface::use(const std::string& iface_name) {
 
 
         }
         }
 
 
-    } else if (getFamily() == V4) {
+    } else if (family == AF_INET) {
         isc_throw(InvalidIfaceName, "unicast addresses in the format of: "
         isc_throw(InvalidIfaceName, "unicast addresses in the format of: "
                   "iface-name/unicast-addr_stress can only be specified for"
                   "iface-name/unicast-addr_stress can only be specified for"
                   " IPv6 addr_stress family");
                   " IPv6 addr_stress family");

+ 39 - 29
src/lib/dhcpsrv/cfg_iface.h

@@ -64,6 +64,11 @@ public:
 ///
 ///
 /// Once interfaces have been specified the sockets (either IPv4 or IPv6)
 /// Once interfaces have been specified the sockets (either IPv4 or IPv6)
 /// can be opened by calling @c CfgIface::openSockets function.
 /// can be opened by calling @c CfgIface::openSockets function.
+///
+/// @warning This class makes use of the AF_INET and AF_INET6 family literals,
+/// but it doesn't verify that the address family value passed as @c uint16_t
+/// parameter is equal to one of them. It is a callers responsibility to
+/// guarantee that the address family value is correct.
 class CfgIface {
 class CfgIface {
 public:
 public:
     /// @brief Keyword used to enable all interfaces.
     /// @brief Keyword used to enable all interfaces.
@@ -72,26 +77,18 @@ public:
     /// that DHCP server should listen on all interfaces.
     /// that DHCP server should listen on all interfaces.
     static const char* ALL_IFACES_KEYWORD;
     static const char* ALL_IFACES_KEYWORD;
 
 
-    /// @brief Protocol family: IPv4 or IPv6.
-    ///
-    /// Depending on the family specified, the IPv4 or IPv6 sockets are
-    /// opened.
-    enum Family {
-        V4, V6
-    };
-
     /// @brief Constructor.
     /// @brief Constructor.
-    ///
-    /// @param family Protocol family (default is V4).
-    CfgIface(Family family = V4);
+    CfgIface();
 
 
     /// @brief Convenience function which closes all open sockets.
     /// @brief Convenience function which closes all open sockets.
-    void closeSockets();
+    void closeSockets() const;
 
 
-    /// @brief Returns protocol family used by the @c CfgIface.
-    Family getFamily() const {
-        return (family_);
-    }
+    /// @brief Compares two @c CfgIface objects for equality.
+    ///
+    /// @param other An object to be compared with this object.
+    ///
+    /// @return true if objects are equal, false otherwise.
+    bool equals(const CfgIface& other) const;
 
 
     /// @brief Tries to open sockets on selected interfaces.
     /// @brief Tries to open sockets on selected interfaces.
     ///
     ///
@@ -100,24 +97,19 @@ public:
     /// documentation for details how to specify interfaces and unicast
     /// documentation for details how to specify interfaces and unicast
     /// addresses to bind the sockets to.
     /// addresses to bind the sockets to.
     ///
     ///
+    /// @param family Address family (AF_INET or AF_INET6).
     /// @param port Port number to be used to bind sockets to.
     /// @param port Port number to be used to bind sockets to.
     /// @param use_bcast A boolean flag which indicates if the broadcast
     /// @param use_bcast A boolean flag which indicates if the broadcast
     /// traffic should be received through the socket. This parameter is
     /// traffic should be received through the socket. This parameter is
     /// ignored for IPv6.
     /// ignored for IPv6.
-    void openSockets(const uint16_t port, const bool use_bcast = true);
+    void openSockets(const uint16_t family, const uint16_t port,
+                     const bool use_bcast = true) const;
 
 
     /// @brief Puts the interface configuration into default state.
     /// @brief Puts the interface configuration into default state.
     ///
     ///
     /// This function removes interface names from the set.
     /// This function removes interface names from the set.
     void reset();
     void reset();
 
 
-    /// @brief Sets protocol family.
-    ///
-    /// @param family New family value (V4 or V6).
-    void setFamily(Family family) {
-        family_ = family;
-    }
-
     /// @brief Select interface to be used to receive DHCP traffic.
     /// @brief Select interface to be used to receive DHCP traffic.
     ///
     ///
     /// This function controls the selection of the interface on which the
     /// This function controls the selection of the interface on which the
@@ -137,6 +129,7 @@ public:
     /// not allowed when specifying a unicast address. For example:
     /// not allowed when specifying a unicast address. For example:
     /// */2001:db8:1::1 is not allowed.
     /// */2001:db8:1::1 is not allowed.
     ///
     ///
+    /// @param family Address family (AF_INET or AF_INET6).
     /// @param iface_name Explicit interface name, a wildcard name (*) of
     /// @param iface_name Explicit interface name, a wildcard name (*) of
     /// the interface(s) or the pair of iterface/unicast-address to be used
     /// the interface(s) or the pair of iterface/unicast-address to be used
     /// to receive DHCP traffic.
     /// to receive DHCP traffic.
@@ -148,7 +141,25 @@ public:
     /// @throw DuplicateIfaceName If the interface is already selected, i.e.
     /// @throw DuplicateIfaceName If the interface is already selected, i.e.
     /// @throw IOError when specified unicast address is invalid.
     /// @throw IOError when specified unicast address is invalid.
     /// @c CfgIface::use has been already called for this interface.
     /// @c CfgIface::use has been already called for this interface.
-    void use(const std::string& iface_name);
+    void use(const uint16_t family, const std::string& iface_name);
+
+    /// @brief Equality operator.
+    ///
+    /// @param other Object to be compared with this object.
+    ///
+    /// @return true if objects are equal, false otherwise.
+    bool operator==(const CfgIface& other) const {
+        return (equals(other));
+    }
+
+    /// @brief Inequality operator.
+    ///
+    /// @param other Object to be compared with this object.
+    ///
+    /// @return true if objects are not equal, false otherwise.
+    bool operator!=(const CfgIface& other) const {
+        return (!equals(other));
+    }
 
 
 private:
 private:
 
 
@@ -157,12 +168,14 @@ private:
     /// This function selects all interfaces to receive DHCP traffic or
     /// This function selects all interfaces to receive DHCP traffic or
     /// deselects all interfaces so as none of them receives a DHCP traffic.
     /// deselects all interfaces so as none of them receives a DHCP traffic.
     ///
     ///
+    /// @param family Address family (AF_INET or AF_INET6).
     /// @param inactive A boolean value which indicates if all interfaces
     /// @param inactive A boolean value which indicates if all interfaces
     /// (except loopback) should be selected or deselected.
     /// (except loopback) should be selected or deselected.
     /// @param loopback_inactive A boolean value which indicates if loopback
     /// @param loopback_inactive A boolean value which indicates if loopback
     /// interface should be selected or deselected.
     /// interface should be selected or deselected.
     /// should be deselected/inactive (true) or selected/active (false).
     /// should be deselected/inactive (true) or selected/active (false).
-    void setState(const bool inactive, const bool loopback_inactive);
+    void setState(const uint16_t family, const bool inactive,
+                  const bool loopback_inactive) const;
 
 
     /// @brief Error handler for executed when opening a socket fail.
     /// @brief Error handler for executed when opening a socket fail.
     ///
     ///
@@ -174,9 +187,6 @@ private:
     /// @param errmsg Error message being logged by the function.
     /// @param errmsg Error message being logged by the function.
     static void socketOpenErrorHandler(const std::string& errmsg);
     static void socketOpenErrorHandler(const std::string& errmsg);
 
 
-    /// @brief Protocol family.
-    Family family_;
-
     /// @brief Represents a set of interface names.
     /// @brief Represents a set of interface names.
     typedef std::set<std::string> IfaceSet;
     typedef std::set<std::string> IfaceSet;
 
 

+ 86 - 3
src/lib/dhcpsrv/cfgmgr.cc

@@ -25,6 +25,8 @@ using namespace isc::util;
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
 
 
+const size_t CfgMgr::CONFIG_LIST_SIZE = 10;
+
 CfgMgr&
 CfgMgr&
 CfgMgr::instance() {
 CfgMgr::instance() {
     static CfgMgr cfg_mgr;
     static CfgMgr cfg_mgr;
@@ -357,14 +359,95 @@ CfgMgr::getD2ClientMgr() {
     return (d2_client_mgr_);
     return (d2_client_mgr_);
 }
 }
 
 
-ConfigurationPtr
-CfgMgr::getConfiguration() {
+void
+CfgMgr::ensureCurrentAllocated() {
+    if (!configuration_ || configs_.empty()) {
+        configuration_.reset(new SrvConfig());
+        configs_.push_back(configuration_);
+    }
+}
+
+void
+CfgMgr::clear() {
+    configs_.clear();
+    ensureCurrentAllocated();
+}
+
+void
+CfgMgr::commit() {
+    ensureCurrentAllocated();
+    if (!configs_.back()->sequenceEquals(*configuration_)) {
+        configuration_ = configs_.back();
+        // Keep track of the maximum size of the configs history. Before adding
+        // new element, we have to remove the oldest one.
+        if (configs_.size() > CONFIG_LIST_SIZE) {
+            SrvConfigList::iterator it = configs_.begin();
+            std::advance(it, configs_.size() - CONFIG_LIST_SIZE);
+            configs_.erase(configs_.begin(), it);
+        }
+    }
+}
+
+void
+CfgMgr::rollback() {
+    ensureCurrentAllocated();
+    if (!configuration_->sequenceEquals(*configs_.back())) {
+        configs_.pop_back();
+    }
+}
+
+void
+CfgMgr::revert(const size_t index) {
+    ensureCurrentAllocated();
+    if (index == 0) {
+        isc_throw(isc::OutOfRange, "invalid commit index 0 when reverting"
+                  " to an old configuration");
+    } else if (index > configs_.size() - 1) {
+        isc_throw(isc::OutOfRange, "unable to revert to commit index '"
+                  << index << "', only '" << configs_.size() - 1
+                  << "' previous commits available");
+    }
+
+    // Let's rollback an existing configuration to make sure that the last
+    // configuration on the list is the current one. Note that all remaining
+    // operations in this function should be exception free so there shouldn't
+    // be a problem that the revert operation fails and the staging
+    // configuration is destroyed by this rollback.
+    rollback();
+
+    // Get the iterator to the current configuration and then advance to the
+    // desired one.
+    SrvConfigList::const_reverse_iterator it = configs_.rbegin();
+    std::advance(it, index);
+
+    // Copy the desired configuration to the new staging configuration. The
+    // staging configuration is re-created here because we rolled back earlier
+    // in this function.
+    (*it)->copy(*getStagingCfg());
+
+    // Make the staging configuration a current one.
+    commit();
+}
+
+ConstSrvConfigPtr
+CfgMgr::getCurrentCfg() {
+    ensureCurrentAllocated();
     return (configuration_);
     return (configuration_);
 }
 }
 
 
+SrvConfigPtr
+CfgMgr::getStagingCfg() {
+    ensureCurrentAllocated();
+    if (configuration_->sequenceEquals(*configs_.back())) {
+        uint32_t sequence = configuration_->getSequence();
+        configs_.push_back(SrvConfigPtr(new SrvConfig(++sequence)));
+    }
+    return (configs_.back());
+}
+
 CfgMgr::CfgMgr()
 CfgMgr::CfgMgr()
     : datadir_(DHCP_DATA_DIR), echo_v4_client_id_(true),
     : datadir_(DHCP_DATA_DIR), echo_v4_client_id_(true),
-      d2_client_mgr_(), configuration_(new Configuration()) {
+      d2_client_mgr_(), verbose_mode_(false) {
     // DHCP_DATA_DIR must be set set with -DDHCP_DATA_DIR="..." in Makefile.am
     // DHCP_DATA_DIR must be set set with -DDHCP_DATA_DIR="..." in Makefile.am
     // Note: the definition of DHCP_DATA_DIR needs to include quotation marks
     // Note: the definition of DHCP_DATA_DIR needs to include quotation marks
     // See AM_CPPFLAGS definition in Makefile.am
     // See AM_CPPFLAGS definition in Makefile.am

+ 153 - 6
src/lib/dhcpsrv/cfgmgr.h

@@ -24,7 +24,7 @@
 #include <dhcpsrv/option_space_container.h>
 #include <dhcpsrv/option_space_container.h>
 #include <dhcpsrv/pool.h>
 #include <dhcpsrv/pool.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/subnet.h>
-#include <dhcpsrv/configuration.h>
+#include <dhcpsrv/srv_config.h>
 #include <util/buffer.h>
 #include <util/buffer.h>
 
 
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
@@ -95,6 +95,11 @@ public:
 class CfgMgr : public boost::noncopyable {
 class CfgMgr : public boost::noncopyable {
 public:
 public:
 
 
+    /// @brief A number of configurations held by @c CfgMgr.
+    ///
+    /// @todo Make it configurable.
+    static const size_t CONFIG_LIST_SIZE;
+
     /// @brief returns a single instance of Configuration Manager
     /// @brief returns a single instance of Configuration Manager
     ///
     ///
     /// CfgMgr is a singleton and this method is the only way of
     /// CfgMgr is a singleton and this method is the only way of
@@ -373,11 +378,130 @@ public:
     /// @return a reference to the DHCP-DDNS manager.
     /// @return a reference to the DHCP-DDNS manager.
     D2ClientMgr& getD2ClientMgr();
     D2ClientMgr& getD2ClientMgr();
 
 
+    /// @name Methods managing the collection of configurations.
+    ///
+    /// The following methods manage the process of preparing a configuration
+    /// without affecting a currently used configuration and then commiting
+    /// the configuration to replace current configuration atomically.
+    /// They also allow for keeping a history of previous configurations so
+    /// as the @c CfgMgr can revert to the historical configuration when
+    /// required.
+    ///
+    /// @todo Migrate all configuration parameters to use the model supported
+    /// by these functions.
+    ///
+    /// @todo Make the size of the configurations history configurable.
+    ///
+    //@{
+
+    /// @brief Removes current, staging and all previous configurations.
+    ///
+    /// This function removes all configurations, including current and
+    /// staging configurations. It creates a new current configuration with
+    /// default settings.
+    ///
+    /// This function is exception safe.
+    void clear();
+
+    /// @brief Commits the staging configuration.
+    ///
+    /// The staging configuration becomes current configuration when this
+    /// function is called. It removes the oldest configuration held in the
+    /// history so as the size of the list of configuration does not exceed
+    /// the @c CONFIG_LIST_SIZE.
+    ///
+    /// This function is exception safe.
+    void commit();
+
+    /// @brief Removes staging configuration.
+    ///
+    /// This function should be called when there is a staging configuration
+    /// (likely created in the previous configuration attempt) but the entirely
+    /// new configuration should be created. It removes the existing staging
+    /// configuration and the next call to @c CfgMgr::getStagingCfg will return a
+    /// fresh (default) configuration.
+    ///
+    /// This function is exception safe.
+    void rollback();
+
+    /// @brief Reverts to one of the previous configurations.
+    ///
+    /// This function reverts to selected previous configuration. The previous
+    /// configuration is entirely copied to a new @c SrvConfig instance. This
+    /// new instance has a unique sequence id (sequence id is not copied). The
+    /// previous configuration (being copied) is not modified by this operation.
+    ///
+    /// The configuration to be copied is identified by the index value which
+    /// is the distance between the current (most recent) and desired
+    /// configuration. If the index is out of range an exception is thrown.
+    ///
+    /// @warning Revert operation will rollback any changes to the staging
+    /// configuration (if it exists).
+    ///
+    /// @param index A distance from the current configuration to the
+    /// past configuration to be reverted. The minimal value is 1 which points
+    /// to the nearest configuration.
+    ///
+    /// @throw isc::OutOfRange if the specified index is out of range.
+    void revert(const size_t index);
 
 
-    /// @brief Returns the current configuration.
+    /// @brief Returns a pointer to the current configuration.
     ///
     ///
-    /// @return a pointer to the current configuration.
-    ConfigurationPtr getConfiguration();
+    /// This function returns pointer to the current configuration. If the
+    /// current configuration is not set it will create a default configuration
+    /// and return it. Current configuration returned is read-only.
+    ///
+    /// @return Non-null const pointer to the current configuration.
+    ConstSrvConfigPtr getCurrentCfg();
+
+    /// @brief Returns a pointer to the staging configuration.
+    ///
+    /// The staging configuration is used by the configuration parsers to
+    /// create new configuration. The staging configuration doesn't affect the
+    /// server's operation until it is committed. The staging configuration
+    /// is a non-const object which can be modified by the caller.
+    ///
+    /// Multiple consecutive calls to this function return the same object
+    /// which can be modified from various places of the code (e.g. various
+    /// configuration parsers).
+    ///
+    /// @return non-null pointer to the staging configuration.
+    SrvConfigPtr getStagingCfg();
+
+    //@}
+
+    /// @name Methods setting/accessing global configuration for the process.
+    ///
+    //@{
+    /// @brief Sets verbose mode.
+    ///
+    /// @param verbose A boolean value indicating if the process should run
+    /// in verbose (true) or non-verbose mode.
+    void setVerbose(const bool verbose) {
+        verbose_mode_ = verbose;
+    }
+
+    /// @brief Checks if the process has been run in verbose mode.
+    ///
+    /// @return true if verbose mode enabled, false otherwise.
+    bool isVerbose() const {
+        return (verbose_mode_);
+    }
+
+    /// @brief Sets the default logger name.
+    ///
+    /// This name is used in cases when a user doesn't provide a configuration
+    /// for logger in the Kea configuration file.
+    void setDefaultLoggerName(const std::string& name) {
+        default_logger_name_ = name;
+    }
+
+    /// @brief Returns default logger name.
+    std::string getDefaultLoggerName() const {
+        return (default_logger_name_);
+    }
+
+    //@}
 
 
 protected:
 protected:
 
 
@@ -410,6 +534,13 @@ protected:
 
 
 private:
 private:
 
 
+    /// @brief Checks if current configuration is created and creates it if needed.
+    ///
+    /// This private method is called to ensure that the current configuration
+    /// is created. If current configuration is not set, it creates the
+    /// default current configuration.
+    void ensureCurrentAllocated();
+
     /// @brief Checks that the IPv4 subnet with the given id already exists.
     /// @brief Checks that the IPv4 subnet with the given id already exists.
     ///
     ///
     /// @param subnet Subnet for which this function will check if the other
     /// @param subnet Subnet for which this function will check if the other
@@ -446,13 +577,29 @@ private:
     /// @brief Manages the DHCP-DDNS client and its configuration.
     /// @brief Manages the DHCP-DDNS client and its configuration.
     D2ClientMgr d2_client_mgr_;
     D2ClientMgr d2_client_mgr_;
 
 
-    /// @brief Configuration
+    /// @brief Server configuration
     ///
     ///
     /// This is a structure that will hold all configuration.
     /// This is a structure that will hold all configuration.
     /// @todo: migrate all other parameters to that structure.
     /// @todo: migrate all other parameters to that structure.
     /// @todo: maybe this should be a vector<Configuration>, so we could keep
     /// @todo: maybe this should be a vector<Configuration>, so we could keep
     ///        previous configurations and do a rollback if needed?
     ///        previous configurations and do a rollback if needed?
-    ConfigurationPtr configuration_;
+    SrvConfigPtr configuration_;
+
+    /// @name Configuration List.
+    ///
+    //@{
+    /// @brief Server configuration list type.
+    typedef std::list<SrvConfigPtr> SrvConfigList;
+
+    /// @brief Container holding all previous and current configurations.
+    SrvConfigList configs_;
+    //@}
+
+    /// @brief Indicates if a process has been ran in the verbose mode.
+    bool verbose_mode_;
+
+    /// @brief Default logger name.
+    std::string default_logger_name_;
 };
 };
 
 
 } // namespace isc::dhcp
 } // namespace isc::dhcp

+ 0 - 64
src/lib/dhcpsrv/configuration.cc

@@ -1,64 +0,0 @@
-// Copyright (C) 2014 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
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <dhcpsrv/cfgmgr.h>
-#include <dhcpsrv/configuration.h>
-#include <sstream>
-
-namespace isc {
-namespace dhcp {
-
-std::string
-Configuration::getConfigSummary(const uint32_t selection) const {
-    std::ostringstream s;
-    size_t subnets_num;
-    if ((selection & CFGSEL_SUBNET4) == CFGSEL_SUBNET4) {
-        subnets_num = CfgMgr::instance().getSubnets4()->size();
-        if (subnets_num > 0) {
-            s << "added IPv4 subnets: " << subnets_num;
-        } else {
-            s << "no IPv4 subnets!";
-        }
-        s << "; ";
-    }
-
-    if ((selection & CFGSEL_SUBNET6) == CFGSEL_SUBNET6) {
-        subnets_num = CfgMgr::instance().getSubnets6()->size();
-        if (subnets_num > 0) {
-            s << "added IPv6 subnets: " << subnets_num;
-        } else {
-            s << "no IPv6 subnets!";
-        }
-        s << "; ";
-    }
-
-    if ((selection & CFGSEL_DDNS) == CFGSEL_DDNS) {
-        bool ddns_enabled = CfgMgr::instance().ddnsEnabled();
-        s << "DDNS: " << (ddns_enabled ? "enabled" : "disabled") << "; ";
-    }
-
-    if (s.tellp() == static_cast<std::streampos>(0)) {
-        s << "no config details available";
-    }
-
-    std::string summary = s.str();
-    size_t last_separator_pos = summary.find_last_of(";");
-    if (last_separator_pos == summary.length() - 2) {
-        summary.erase(last_separator_pos);
-    }
-    return (summary);
-}
-
-}
-}

+ 0 - 149
src/lib/dhcpsrv/configuration.h

@@ -1,149 +0,0 @@
-// Copyright (C) 2014 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
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef DHCPSRV_CONFIGURATION_H
-#define DHCPSRV_CONFIGURATION_H
-
-#include <dhcpsrv/cfg_iface.h>
-#include <log/logger_level.h>
-#include <boost/shared_ptr.hpp>
-#include <vector>
-#include <stdint.h>
-
-namespace isc {
-namespace dhcp {
-
-class CfgMgr;
-
-/// @brief Defines single logging destination
-///
-/// This structure is used to keep log4cplus configuration parameters.
-struct LoggingDestination {
-
-    /// @brief defines logging destination output
-    ///
-    /// Values accepted are: stdout, stderr, syslog, syslog:name.
-    /// Any other destination will be considered a file name.
-    std::string output_;
-
-    /// @brief Maximum number of log files in rotation
-    int maxver_;
-
-    /// @brief Maximum log file size
-    uint64_t maxsize_;
-};
-
-/// @brief structure that describes one logging entry
-///
-/// This is a structure that conveys one logger entry configuration.
-/// The structure in JSON form has the following syntax:
-///        {
-///            "name": "*",
-///            "output_options": [
-///                {
-///                    "output": "/path/to/the/logfile.log",
-///                    "maxver": 8,
-///                    "maxsize": 204800
-///                }
-///            ],
-///            "severity": "WARN",
-///            "debuglevel": 99
-///        },
-struct LoggingInfo {
-
-    /// @brief logging name
-    std::string name_;
-
-    /// @brief describes logging severity
-    isc::log::Severity severity_;
-
-    /// @brief debuglevel (used when severity_ == DEBUG)
-    ///
-    /// We use range 0(least verbose)..99(most verbose)
-    int debuglevel_;
-
-    /// @brief specific logging destinations
-    std::vector<LoggingDestination> destinations_;
-};
-
-/// @brief storage for logging information in log4cplus format
-typedef std::vector<isc::dhcp::LoggingInfo> LoggingInfoStorage;
-
-/// @brief Specifies current DHCP configuration
-///
-/// @todo Migrate all other configuration parameters from cfgmgr.h here
-struct Configuration {
-
-    /// @name Constants for selection of parameters returned by @c getConfigSummary
-    ///
-    //@{
-    /// Nothing selected
-    static const uint32_t CFGSEL_NONE    = 0x00000000;
-    /// Number of IPv4 subnets
-    static const uint32_t CFGSEL_SUBNET4 = 0x00000001;
-    /// Number of IPv6 subnets
-    static const uint32_t CFGSEL_SUBNET6 = 0x00000002;
-    /// Number of enabled ifaces
-    static const uint32_t CFGSEL_IFACE4  = 0x00000004;
-    /// Number of v6 ifaces
-    static const uint32_t CFGSEL_IFACE6  = 0x00000008;
-    /// DDNS enabled/disabled
-    static const uint32_t CFGSEL_DDNS    = 0x00000010;
-    /// Number of all subnets
-    static const uint32_t CFGSEL_SUBNET  = 0x00000003;
-    /// IPv4 related config
-    static const uint32_t CFGSEL_ALL4    = 0x00000015;
-    /// IPv6 related config
-    static const uint32_t CFGSEL_ALL6    = 0x0000001A;
-    /// Whole config
-    static const uint32_t CFGSEL_ALL     = 0xFFFFFFFF;
-    //@}
-
-    /// @brief logging specific information
-    LoggingInfoStorage logging_info_;
-
-    /// @brief Interface configuration.
-    ///
-    /// Used to select interfaces on which the DHCP server will listen to
-    /// queries.
-    CfgIface cfg_iface_;
-
-    /// @brief Returns summary of the configuration in the textual format.
-    ///
-    /// This method returns the brief text describing the current configuration.
-    /// It may be used for logging purposes, e.g. when the new configuration is
-    /// committed to notify a user about the changes in configuration.
-    ///
-    /// @todo Currently this method uses @c CfgMgr accessors to get the
-    /// configuration parameters. Once these parameters are migrated from the
-    /// @c CfgMgr this method will have to be modified accordingly.
-    ///
-    /// @todo Implement reporting a summary of interfaces being used for
-    /// receiving and sending DHCP messages. This will be implemented with
-    /// ticket #3512.
-    ///
-    /// @param selection Is a bitfield which describes the parts of the
-    /// configuration to be returned.
-    ///
-    /// @return Summary of the configuration in the textual format.
-    std::string getConfigSummary(const uint32_t selection) const;
-};
-
-/// @brief pointer to the configuration
-typedef boost::shared_ptr<Configuration> ConfigurationPtr;
-
-} // namespace isc::dhcp
-} // namespace isc
-
-#endif // DHCPSRV_CONFIGURATION_H

+ 18 - 32
src/lib/dhcpsrv/daemon.cc

@@ -13,6 +13,7 @@
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
 #include <config.h>
 #include <config.h>
+#include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/daemon.h>
 #include <dhcpsrv/daemon.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 #include <cc/data.h>
 #include <cc/data.h>
@@ -31,7 +32,7 @@ namespace dhcp {
 std::string Daemon::config_file_ = "";
 std::string Daemon::config_file_ = "";
 
 
 Daemon::Daemon()
 Daemon::Daemon()
-    : signal_set_(), signal_handler_(), verbose_(false) {
+    : signal_set_(), signal_handler_() {
 }
 }
 
 
 Daemon::~Daemon() {
 Daemon::~Daemon() {
@@ -56,40 +57,25 @@ void Daemon::handleSignal() {
 }
 }
 
 
 void Daemon::configureLogger(const isc::data::ConstElementPtr& log_config,
 void Daemon::configureLogger(const isc::data::ConstElementPtr& log_config,
-                             const ConfigurationPtr& storage,
-                             bool verbose) {
-
-    // This is utility class that translates JSON structures into formats
-    // understandable by log4cplus.
-    LogConfigParser parser(storage);
-
-    if (!log_config) {
-        // There was no logger configuration. Let's clear any config
-        // and revert to the default.
-
-        parser.applyDefaultConfiguration(verbose); // Set up default logging
-        return;
+                             const SrvConfigPtr& storage) {
+
+    if (log_config) {
+        isc::data::ConstElementPtr loggers = log_config->get("loggers");
+        if (loggers) {
+            LogConfigParser parser(storage);
+            parser.parseConfiguration(loggers, CfgMgr::instance().isVerbose());
+        }
     }
     }
+}
 
 
-    isc::data::ConstElementPtr loggers;
-    loggers = log_config->get("loggers");
-    if (!loggers) {
-        // There is Logging structure, but it doesn't have loggers
-        // array in it. Let's clear any old logging configuration
-        // we may have and revert to the default.
-
-        parser.applyDefaultConfiguration(verbose); // Set up default logging
-        return;
-    }
-
-    // Translate JSON structures into log4cplus formats
-    parser.parseConfiguration(loggers, verbose);
-
-    // Apply the configuration
+void
+Daemon::setVerbose(bool verbose) {
+    CfgMgr::instance().setVerbose(verbose);
+}
 
 
-    /// @todo: Once configuration unrolling is implemented,
-    /// this call will be moved to a separate method.
-    parser.applyConfiguration();
+bool
+Daemon::getVerbose() const {
+    return (CfgMgr::instance().isVerbose());
 }
 }
 
 
 };
 };

+ 4 - 12
src/lib/dhcpsrv/daemon.h

@@ -17,7 +17,7 @@
 
 
 #include <config.h>
 #include <config.h>
 #include <cc/data.h>
 #include <cc/data.h>
-#include <dhcpsrv/configuration.h>
+#include <dhcpsrv/srv_config.h>
 #include <util/signal_set.h>
 #include <util/signal_set.h>
 #include <boost/noncopyable.hpp>
 #include <boost/noncopyable.hpp>
 #include <string>
 #include <string>
@@ -128,10 +128,8 @@ public:
     ///
     ///
     /// @param log_config JSON structures that describe logging
     /// @param log_config JSON structures that describe logging
     /// @param storage configuration will be stored here
     /// @param storage configuration will be stored here
-    /// @param verbose specifies if verbose mode should be enabled
     static void configureLogger(const isc::data::ConstElementPtr& log_config,
     static void configureLogger(const isc::data::ConstElementPtr& log_config,
-                                const isc::dhcp::ConfigurationPtr& storage,
-                                bool verbose);
+                                const isc::dhcp::SrvConfigPtr& storage);
 
 
     /// @brief Sets or clears verbose mode
     /// @brief Sets or clears verbose mode
     ///
     ///
@@ -140,16 +138,12 @@ public:
     /// config file are ignored.
     /// config file are ignored.
     ///
     ///
     /// @param verbose specifies if verbose should be set or not
     /// @param verbose specifies if verbose should be set or not
-    void setVerbose(bool verbose) {
-        verbose_ = verbose;
-    }
+    void setVerbose(const bool verbose);
 
 
     /// @brief Returns if running in verbose mode
     /// @brief Returns if running in verbose mode
     ///
     ///
     /// @return verbose mode
     /// @return verbose mode
-    bool getVerbose() const {
-        return (verbose_);
-    }
+    bool getVerbose() const;
 
 
     /// @brief returns Kea version on stdout and exits.
     /// @brief returns Kea version on stdout and exits.
     ///
     ///
@@ -195,8 +189,6 @@ private:
     /// @brief Config file name or empty if config file not used.
     /// @brief Config file name or empty if config file not used.
     static std::string config_file_;
     static std::string config_file_;
 
 
-    /// @brief Verbose mode
-    bool verbose_;
 };
 };
 
 
 }; // end of isc::dhcp namespace
 }; // end of isc::dhcp namespace

+ 8 - 21
src/lib/dhcpsrv/dhcp_parsers.cc

@@ -179,8 +179,9 @@ template <> void ValueParser<std::string>::build(ConstElementPtr value) {
 // ******************** InterfaceListConfigParser *************************
 // ******************** InterfaceListConfigParser *************************
 
 
 InterfaceListConfigParser::
 InterfaceListConfigParser::
-InterfaceListConfigParser(const std::string& param_name)
-    : param_name_(param_name) {
+InterfaceListConfigParser(const std::string& param_name,
+                          ParserContextPtr global_context)
+    : param_name_(param_name), global_context_(global_context) {
     if (param_name_ != "interfaces") {
     if (param_name_ != "interfaces") {
         isc_throw(BadValue, "Internal error. Interface configuration "
         isc_throw(BadValue, "Internal error. Interface configuration "
             "parser called for the wrong parameter: " << param_name);
             "parser called for the wrong parameter: " << param_name);
@@ -189,38 +190,24 @@ InterfaceListConfigParser(const std::string& param_name)
 
 
 void
 void
 InterfaceListConfigParser::build(ConstElementPtr value) {
 InterfaceListConfigParser::build(ConstElementPtr value) {
-    // Copy the current interface configuration.
-    ConfigurationPtr config = CfgMgr::instance().getConfiguration();
-    cfg_iface_ = config->cfg_iface_;
-    cfg_iface_.reset();
+    CfgIface cfg_iface;
     BOOST_FOREACH(ConstElementPtr iface, value->listValue()) {
     BOOST_FOREACH(ConstElementPtr iface, value->listValue()) {
         std::string iface_name = iface->stringValue();
         std::string iface_name = iface->stringValue();
         try {
         try {
-            cfg_iface_.use(iface_name);
+            cfg_iface.use(global_context_->universe_ == Option::V4 ?
+                          AF_INET : AF_INET6, iface_name);
 
 
         } catch (const std::exception& ex) {
         } catch (const std::exception& ex) {
             isc_throw(DhcpConfigError, "Failed to select interface: "
             isc_throw(DhcpConfigError, "Failed to select interface: "
                       << ex.what() << " (" << value->getPosition() << ")");
                       << ex.what() << " (" << value->getPosition() << ")");
         }
         }
     }
     }
+    CfgMgr::instance().getStagingCfg()->setCfgIface(cfg_iface);
 }
 }
 
 
 void
 void
 InterfaceListConfigParser::commit() {
 InterfaceListConfigParser::commit() {
-    // Use the new configuration created in a build time.
-    CfgMgr::instance().getConfiguration()->cfg_iface_ = cfg_iface_;
-}
-
-bool
-InterfaceListConfigParser::isIfaceAdded(const std::string& iface) const {
-
-    for (IfaceListStorage::const_iterator it = interfaces_.begin();
-         it != interfaces_.end(); ++it) {
-        if (iface == *it) {
-            return (true);
-        }
-    }
-    return (false);
+    // Nothing to do.
 }
 }
 
 
 // ******************** HooksLibrariesParser *************************
 // ******************** HooksLibrariesParser *************************

+ 6 - 17
src/lib/dhcpsrv/dhcp_parsers.h

@@ -396,8 +396,10 @@ public:
     /// "interface" parameter only. All other types will throw exception.
     /// "interface" parameter only. All other types will throw exception.
     ///
     ///
     /// @param param_name name of the configuration parameter being parsed
     /// @param param_name name of the configuration parameter being parsed
+    /// @param global_context Global parser context.
     /// @throw BadValue if supplied parameter name is not "interface"
     /// @throw BadValue if supplied parameter name is not "interface"
-    InterfaceListConfigParser(const std::string& param_name);
+    InterfaceListConfigParser(const std::string& param_name,
+                              ParserContextPtr global_context);
 
 
     /// @brief parses parameters value
     /// @brief parses parameters value
     ///
     ///
@@ -407,29 +409,16 @@ public:
     /// @param value pointer to the content of parsed values
     /// @param value pointer to the content of parsed values
     virtual void build(isc::data::ConstElementPtr value);
     virtual void build(isc::data::ConstElementPtr value);
 
 
-    /// @brief Assignes a parsed list of interfaces to the configuration.
-    ///
-    /// This is exception safe operation.
+    /// @brief Does nothing.
     virtual void commit();
     virtual void commit();
 
 
 private:
 private:
-    /// @brief Check that specified interface exists in
-    /// @c InterfaceListConfigParser::interfaces_.
-    ///
-    /// @param iface A name of the interface.
-    ///
-    /// @return true if specified interface name was found.
-    bool isIfaceAdded(const std::string& iface) const;
-
-    /// contains list of network interfaces
-    typedef std::list<std::string> IfaceListStorage;
-    IfaceListStorage interfaces_;
 
 
     // Parsed parameter name
     // Parsed parameter name
     std::string param_name_;
     std::string param_name_;
 
 
-    /// Holds the configuration created during
-    CfgIface cfg_iface_;
+    /// Global parser context.
+    ParserContextPtr global_context_;
 };
 };
 
 
 /// @brief Parser for hooks library list
 /// @brief Parser for hooks library list

+ 42 - 6
src/lib/dhcpsrv/libdhcpsrv.dox

@@ -1,4 +1,4 @@
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012, 2014 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -39,11 +39,47 @@ only available backend is MySQL (see \ref isc::dhcp::MySqlLeaseMgr).
 
 
 @section cfgmgr Configuration Manager
 @section cfgmgr Configuration Manager
 
 
-Configuration Manager (\ref isc::dhcp::CfgMgr) stores configuration information
-necessary for DHCPv4 and DHCPv6 server operation. In particular, it stores
-subnets (\ref isc::dhcp::Subnet4 and \ref isc::dhcp::Subnet6) together with
-their pools (\ref isc::dhcp::Pool4 and \ref isc::dhcp::Pool6), options and
-other information specified by the user in the Kea configuration.
+Configuration Manager (\ref isc::dhcp::CfgMgr) is a singleton object which
+holds configuration information necessary for the operation of Kea daemons.
+A complete collection of information for the daemon is stored in the
+\ref isc::dhcp::SrvConfig object. Internally, the Configuration Manager
+holds a list of \ref isc::dhcp::SrvConfig objects, from which one
+is marked as "current configuration".
+
+When the server starts up or is being reconfigured a new
+\ref isc::dhcp::SrvConfig object, referred to as "staging configuration",
+is created. The staging configuration is held at the tip of the list of
+configurations. The object can be accessed by calling the
+\ref isc::dhcp::CfgMgr::getStagingCfg. This object can be accessed
+from different stages of the configuration parsing and modified as needed.
+Modifications of the staging configuration do not affect the current
+configuration. The staging configuration is unused until the
+\ref isc::dhcp::CfgMgr::commit function is called. This exception safe method
+marks the staging object as "current configuration". The const pointer to the
+current configuration can be accessed by calling a
+\ref isc::dhcp::CfgMgr::getCurrentCfg.
+
+The staging configuration can be discarded at any time before it is committed
+by calling the \ref isc::dhcp::CfgMgr::rollback. This removes the
+\ref isc::dhcp::SrvConfig object from the Configuration Manager. When
+the \ref isc::dhcp::CfgMgr::getStagingCfg is called again a fresh/default
+\ref isc::dhcp::SrvConfig object is returned.
+
+The Configuration Manager stores previous configurations, i.e. configurations
+which occurred prior to the most current configuration. This is currently
+unused (except for unit tests) by the deamons, but in the future this
+mechanism can be used to trigger a rollover of the server configuration
+to a last good configuration that the administrator prefers.
+
+The previous configurations are identified by the value which specifies a
+distance between the current configuration and the previous
+configuration. For example: the value of 1 identifies an immediate
+predecessor of the current configuration, the value of 2 identifies the
+one that occurred before it etc.
+
+@todo Currently, only a subset of configuration information is stored in
+the \ref isc::dhcp::SrvConfig object. Kea developers are actively working
+on migrating the other configuration parameters to it.
 
 
 @section allocengine Allocation Engine
 @section allocengine Allocation Engine
 
 

+ 8 - 4
src/lib/dhcpsrv/logging.cc

@@ -26,7 +26,7 @@ using namespace isc::log;
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
 
 
-LogConfigParser::LogConfigParser(const ConfigurationPtr& storage)
+LogConfigParser::LogConfigParser(const SrvConfigPtr& storage)
     :config_(storage), verbose_(false) {
     :config_(storage), verbose_(false) {
     if (!storage) {
     if (!storage) {
         isc_throw(BadValue, "LogConfigParser needs a pointer to the "
         isc_throw(BadValue, "LogConfigParser needs a pointer to the "
@@ -55,6 +55,8 @@ void LogConfigParser::parseConfigEntry(isc::data::ConstElementPtr entry) {
     }
     }
 
 
     LoggingInfo info;
     LoggingInfo info;
+    // Remove default destinations as we are going to replace them.
+    info.clearDestinations();
 
 
     // Get a name
     // Get a name
     isc::data::ConstElementPtr name_ptr = entry->get("name");
     isc::data::ConstElementPtr name_ptr = entry->get("name");
@@ -113,7 +115,7 @@ void LogConfigParser::parseConfigEntry(isc::data::ConstElementPtr entry) {
         parseOutputOptions(info.destinations_, output_options);
         parseOutputOptions(info.destinations_, output_options);
     }
     }
     
     
-    config_->logging_info_.push_back(info);
+    config_->addLoggingInfo(info);
 }
 }
 
 
 void LogConfigParser::parseOutputOptions(std::vector<LoggingDestination>& destination,
 void LogConfigParser::parseOutputOptions(std::vector<LoggingDestination>& destination,
@@ -121,6 +123,7 @@ void LogConfigParser::parseOutputOptions(std::vector<LoggingDestination>& destin
     if (!output_options) {
     if (!output_options) {
         isc_throw(BadValue, "Missing 'output_options' structure in 'loggers'");
         isc_throw(BadValue, "Missing 'output_options' structure in 'loggers'");
     }
     }
+
     BOOST_FOREACH(ConstElementPtr output_option, output_options->listValue()) {
     BOOST_FOREACH(ConstElementPtr output_option, output_options->listValue()) {
 
 
         LoggingDestination dest;
         LoggingDestination dest;
@@ -159,8 +162,9 @@ void LogConfigParser::applyConfiguration() {
     std::vector<LoggerSpecification> specs;
     std::vector<LoggerSpecification> specs;
 
 
     // Now iterate through all specified loggers
     // Now iterate through all specified loggers
-    for (LoggingInfoStorage::const_iterator it = config_->logging_info_.begin();
-         it != config_->logging_info_.end(); ++it) {
+    const LoggingInfoStorage& logging_info = config_->getLoggingInfo();
+    for (LoggingInfoStorage::const_iterator it = logging_info.begin();
+         it != logging_info.end(); ++it) {
 
 
         // Prepare the objects to define the logging specification
         // Prepare the objects to define the logging specification
         LoggerSpecification spec(it->name_,
         LoggerSpecification spec(it->name_,

+ 3 - 3
src/lib/dhcpsrv/logging.h

@@ -16,7 +16,7 @@
 #define DHCPSRV_LOGGING_H
 #define DHCPSRV_LOGGING_H
 
 
 #include <cc/data.h>
 #include <cc/data.h>
-#include <dhcpsrv/configuration.h>
+#include <dhcpsrv/srv_config.h>
 #include <vector>
 #include <vector>
 
 
 namespace isc {
 namespace isc {
@@ -52,7 +52,7 @@ public:
     /// @brief Constructor
     /// @brief Constructor
     ///
     ///
     /// @param storage parsed logging configuration will be stored here
     /// @param storage parsed logging configuration will be stored here
-    LogConfigParser(const ConfigurationPtr& storage);
+    LogConfigParser(const SrvConfigPtr& storage);
 
 
     /// @brief Parses specified configuration
     /// @brief Parses specified configuration
     ///
     ///
@@ -101,7 +101,7 @@ private:
     /// @brief Configuration is stored here
     /// @brief Configuration is stored here
     ///
     ///
     /// LogConfigParser class uses only config_->logging_info_ field.
     /// LogConfigParser class uses only config_->logging_info_ field.
-    ConfigurationPtr config_;
+    SrvConfigPtr config_;
 
 
     /// @brief Verbose mode
     /// @brief Verbose mode
     ///
     ///

+ 145 - 0
src/lib/dhcpsrv/logging_info.cc

@@ -0,0 +1,145 @@
+// Copyright (C) 2014 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
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/logging_info.h>
+#include <log/logger_name.h>
+
+using namespace isc::log;
+
+namespace isc {
+namespace dhcp {
+
+bool
+LoggingDestination::equals(const LoggingDestination& other) const {
+    return (output_ == other.output_ &&
+            maxver_ == other.maxver_ &&
+            maxsize_ == other.maxsize_);
+}
+
+LoggingInfo::LoggingInfo()
+    : name_("kea"), severity_(isc::log::INFO), debuglevel_(0) {
+    // If configuration Manager is in the verbose mode, we need to modify the
+    // default settings.
+    if (CfgMgr::instance().isVerbose()) {
+        severity_ = isc::log::DEBUG;
+        debuglevel_ = 99;
+    }
+
+    // If the process has set the non-empty name for the default logger,
+    // let's use this name.
+    std::string logger_name = CfgMgr::instance().getDefaultLoggerName();
+    if (!logger_name.empty()) {
+        name_ = logger_name;
+    }
+
+    // Add a default logging destination in case use hasn't provided a
+    // logger specification.
+    LoggingDestination dest;
+    dest.output_ = "stdout";
+    destinations_.push_back(dest);
+}
+
+bool
+LoggingInfo::equals(const LoggingInfo& other) const {
+    // If number of destinations aren't equal, the objects are not equal.
+    if (destinations_.size() != other.destinations_.size()) {
+        return (false);
+    }
+    // If there is the same number of logging destinations verify that the
+    // destinations are equal. The order doesn't matter to we don't expect
+    // that they are at the same index of the vectors.
+    for (std::vector<LoggingDestination>::const_iterator
+             it_this = destinations_.begin();
+         it_this != destinations_.end();
+         ++it_this) {
+        bool match = false;
+        for (std::vector<LoggingDestination>::const_iterator
+                 it_other = other.destinations_.begin();
+             it_other != other.destinations_.end();
+             ++it_other) {
+            if (it_this->equals(*it_other)) {
+                match = true;
+                break;
+            }
+        }
+        if (!match) {
+            return (false);
+        }
+    }
+
+    // Logging destinations are equal. Check the rest of the parameters for
+    // equality.
+    return (name_ == other.name_ &&
+            severity_ == other.severity_ &&
+            debuglevel_ == other.debuglevel_);
+}
+
+LoggerSpecification
+LoggingInfo::toSpec() const {
+    static const std::string STDOUT = "stdout";
+    static const std::string STDERR = "stderr";
+    static const std::string SYSLOG = "syslog";
+    static const std::string SYSLOG_COLON = "syslog:";
+
+    LoggerSpecification spec(name_, severity_, debuglevel_);
+
+    // Go over logger destinations and create output options accordinly.
+    for (std::vector<LoggingDestination>::const_iterator dest =
+             destinations_.begin(); dest != destinations_.end(); ++dest) {
+
+        OutputOption option;
+        // Set up output option according to destination specification
+        if (dest->output_ == STDOUT) {
+            option.destination = OutputOption::DEST_CONSOLE;
+            option.stream = OutputOption::STR_STDOUT;
+
+        } else if (dest->output_ == STDERR) {
+            option.destination = OutputOption::DEST_CONSOLE;
+            option.stream = OutputOption::STR_STDERR;
+
+        } else if (dest->output_ == SYSLOG) {
+            option.destination = OutputOption::DEST_SYSLOG;
+            // Use default specified in OutputOption constructor for the
+            // syslog destination
+
+        } else if (dest->output_.find(SYSLOG_COLON) == 0) {
+            option.destination = OutputOption::DEST_SYSLOG;
+            // Must take account of the string actually being "syslog:"
+            if (dest->output_ == SYSLOG_COLON) {
+                // The expected syntax is syslog:facility. User skipped
+                // the logging name, so we'll just use the default ("kea")
+                option.facility = isc::log::getDefaultRootLoggerName();
+
+            } else {
+                // Everything else in the string is the facility name
+                option.facility = dest->output_.substr(SYSLOG_COLON.size());
+            }
+
+        } else {
+            // Not a recognised destination, assume a file.
+            option.destination = OutputOption::DEST_FILE;
+            option.filename = dest->output_;
+        }
+
+        // ... and set the destination
+        spec.addOutputOption(option);
+    }
+
+    return (spec);
+}
+
+} // end of namespace isc::dhcp
+} // end of namespace isc

+ 131 - 0
src/lib/dhcpsrv/logging_info.h

@@ -0,0 +1,131 @@
+// Copyright (C) 2014 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
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DHCPSRV_LOGGING_INFO_H
+#define DHCPSRV_LOGGING_INFO_H
+
+#include <log/logger_level.h>
+#include <log/logger_specification.h>
+#include <stdint.h>
+#include <vector>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Defines single logging destination
+///
+/// This structure is used to keep log4cplus configuration parameters.
+struct LoggingDestination {
+
+    /// @brief defines logging destination output
+    ///
+    /// Values accepted are: stdout, stderr, syslog, syslog:name.
+    /// Any other destination will be considered a file name.
+    std::string output_;
+
+    /// @brief Maximum number of log files in rotation
+    int maxver_;
+
+    /// @brief Maximum log file size
+    uint64_t maxsize_;
+
+    /// @brief Compares two objects for equality.
+    ///
+    /// @param other Object to be compared with this object.
+    ///
+    /// @return true if objects are equal, false otherwise.
+    bool equals(const LoggingDestination& other) const;
+
+    /// @brief Default constructor.
+    LoggingDestination()
+        : output_("stdout"), maxver_(1), maxsize_(204800) {
+    }
+};
+
+/// @brief structure that describes one logging entry
+///
+/// This is a structure that conveys one logger entry configuration.
+/// The structure in JSON form has the following syntax:
+///        {
+///            "name": "*",
+///            "output_options": [
+///                {
+///                    "output": "/path/to/the/logfile.log",
+///                    "maxver": 8,
+///                    "maxsize": 204800
+///                }
+///            ],
+///            "severity": "WARN",
+///            "debuglevel": 99
+///        },
+struct LoggingInfo {
+
+    /// @brief logging name
+    std::string name_;
+
+    /// @brief describes logging severity
+    isc::log::Severity severity_;
+
+    /// @brief debuglevel (used when severity_ == DEBUG)
+    ///
+    /// We use range 0(least verbose)..99(most verbose)
+    int debuglevel_;
+
+    /// @brief specific logging destinations
+    std::vector<LoggingDestination> destinations_;
+
+    /// @brief Default constructor.
+    LoggingInfo();
+
+    /// @brief Removes logging destinations.
+    void clearDestinations() {
+        destinations_.clear();
+    }
+
+    /// @brief Compares two objects for equality.
+    ///
+    /// @param other An object to be compared with this object.
+    ///
+    /// @return true if objects are equal, false otherwise.
+    bool equals(const LoggingInfo& other) const;
+
+    /// @brief Compares two objects for equality.
+    ///
+    /// @param other An object to be compared with this object.
+    ///
+    /// @return true if objects are equal, false otherwise.
+    bool operator==(const LoggingInfo& other) const {
+        return (equals(other));
+    }
+
+    /// @brief Compares two objects for inequality.
+    ///
+    /// @param other An object to be compared with this object.
+    ///
+    /// @return true if objects are not equal, false otherwise.
+    bool operator!=(const LoggingInfo& other) const {
+        return (!equals(other));
+    }
+
+    /// @brief Converts logger configuration to a spec.
+    isc::log::LoggerSpecification toSpec() const;
+};
+
+/// @brief storage for logging information in log4cplus format
+typedef std::vector<isc::dhcp::LoggingInfo> LoggingInfoStorage;
+
+}
+}
+
+#endif // DHCPSRV_LOGGING_INFO_H

+ 138 - 0
src/lib/dhcpsrv/srv_config.cc

@@ -0,0 +1,138 @@
+// Copyright (C) 2014 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
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/srv_config.h>
+#include <log/logger_manager.h>
+#include <log/logger_specification.h>
+#include <list>
+#include <sstream>
+
+using namespace isc::log;
+
+namespace isc {
+namespace dhcp {
+
+SrvConfig::SrvConfig()
+    : sequence_(0) {
+}
+
+SrvConfig::SrvConfig(uint32_t sequence)
+    : sequence_(sequence) {
+}
+
+std::string
+SrvConfig::getConfigSummary(const uint32_t selection) const {
+    std::ostringstream s;
+    size_t subnets_num;
+    if ((selection & CFGSEL_SUBNET4) == CFGSEL_SUBNET4) {
+        subnets_num = CfgMgr::instance().getSubnets4()->size();
+        if (subnets_num > 0) {
+            s << "added IPv4 subnets: " << subnets_num;
+        } else {
+            s << "no IPv4 subnets!";
+        }
+        s << "; ";
+    }
+
+    if ((selection & CFGSEL_SUBNET6) == CFGSEL_SUBNET6) {
+        subnets_num = CfgMgr::instance().getSubnets6()->size();
+        if (subnets_num > 0) {
+            s << "added IPv6 subnets: " << subnets_num;
+        } else {
+            s << "no IPv6 subnets!";
+        }
+        s << "; ";
+    }
+
+    if ((selection & CFGSEL_DDNS) == CFGSEL_DDNS) {
+        bool ddns_enabled = CfgMgr::instance().ddnsEnabled();
+        s << "DDNS: " << (ddns_enabled ? "enabled" : "disabled") << "; ";
+    }
+
+    if (s.tellp() == static_cast<std::streampos>(0)) {
+        s << "no config details available";
+    }
+
+    std::string summary = s.str();
+    size_t last_separator_pos = summary.find_last_of(";");
+    if (last_separator_pos == summary.length() - 2) {
+        summary.erase(last_separator_pos);
+    }
+    return (summary);
+}
+
+bool
+SrvConfig::sequenceEquals(const SrvConfig& other) {
+    return (getSequence() == other.getSequence());
+}
+
+void
+SrvConfig::copy(SrvConfig& new_config) const {
+    // We will entirely replace loggers in the new configuration.
+    new_config.logging_info_.clear();
+    for (LoggingInfoStorage::const_iterator it = logging_info_.begin();
+         it != logging_info_.end(); ++it) {
+        new_config.addLoggingInfo(*it);
+    }
+    // Replace interface configuration.
+    new_config.setCfgIface(cfg_iface_);
+}
+
+void
+SrvConfig::applyLoggingCfg() const {
+    /// @todo Remove the hardcoded location.
+    setenv("KEA_LOCKFILE_DIR_FROM_BUILD", "/tmp", 1);
+
+    std::list<LoggerSpecification> specs;
+    for (LoggingInfoStorage::const_iterator it = logging_info_.begin();
+         it != logging_info_.end(); ++it) {
+        specs.push_back(it->toSpec());
+    }
+    LoggerManager manager;
+    manager.process(specs.begin(), specs.end());
+}
+
+bool
+SrvConfig::equals(const SrvConfig& other) const {
+    // If number of loggers is different, then configurations aren't equal.
+    if (logging_info_.size() != other.logging_info_.size()) {
+        return (false);
+    }
+    // Pass through all loggers and try to find the match for each of them
+    // with the loggers from the other configuration. The order doesn't
+    // matter so we can't simply compare the vectors.
+    for (LoggingInfoStorage::const_iterator this_it =
+             logging_info_.begin(); this_it != logging_info_.end();
+         ++this_it) {
+        bool match = false;
+        for (LoggingInfoStorage::const_iterator other_it =
+                 other.logging_info_.begin();
+             other_it != other.logging_info_.end(); ++other_it) {
+            if (this_it->equals(*other_it)) {
+                match = true;
+                break;
+            }
+        }
+        // No match found for the particular logger so return false.
+        if (!match) {
+            return (false);
+        }
+    }
+    // Logging information is equal between objects, so check other values.
+    return (cfg_iface_ == other.cfg_iface_);
+}
+
+}
+}

+ 230 - 0
src/lib/dhcpsrv/srv_config.h

@@ -0,0 +1,230 @@
+// Copyright (C) 2014 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
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DHCPSRV_CONFIG_H
+#define DHCPSRV_CONFIG_H
+
+#include <dhcpsrv/cfg_iface.h>
+#include <dhcpsrv/logging_info.h>
+#include <boost/shared_ptr.hpp>
+#include <vector>
+#include <stdint.h>
+
+namespace isc {
+namespace dhcp {
+
+class CfgMgr;
+
+
+/// @brief Specifies current DHCP configuration
+///
+/// @todo Migrate all other configuration parameters from cfgmgr.h here
+class SrvConfig {
+public:
+    /// @name Constants for selection of parameters returned by @c getConfigSummary
+    ///
+    //@{
+    /// Nothing selected
+    static const uint32_t CFGSEL_NONE    = 0x00000000;
+    /// Number of IPv4 subnets
+    static const uint32_t CFGSEL_SUBNET4 = 0x00000001;
+    /// Number of IPv6 subnets
+    static const uint32_t CFGSEL_SUBNET6 = 0x00000002;
+    /// Number of enabled ifaces
+    static const uint32_t CFGSEL_IFACE4  = 0x00000004;
+    /// Number of v6 ifaces
+    static const uint32_t CFGSEL_IFACE6  = 0x00000008;
+    /// DDNS enabled/disabled
+    static const uint32_t CFGSEL_DDNS    = 0x00000010;
+    /// Number of all subnets
+    static const uint32_t CFGSEL_SUBNET  = 0x00000003;
+    /// IPv4 related config
+    static const uint32_t CFGSEL_ALL4    = 0x00000015;
+    /// IPv6 related config
+    static const uint32_t CFGSEL_ALL6    = 0x0000001A;
+    /// Whole config
+    static const uint32_t CFGSEL_ALL     = 0xFFFFFFFF;
+    //@}
+
+    /// @brief Default constructor.
+    ///
+    /// This constructor sets configuration sequence number to 0.
+    SrvConfig();
+
+    /// @brief Constructor.
+    ///
+    /// Sets arbitrary configuration sequence number.
+    SrvConfig(uint32_t sequence);
+
+    /// @brief Returns summary of the configuration in the textual format.
+    ///
+    /// This method returns the brief text describing the current configuration.
+    /// It may be used for logging purposes, e.g. when the new configuration is
+    /// committed to notify a user about the changes in configuration.
+    ///
+    /// @todo Currently this method uses @c CfgMgr accessors to get the
+    /// configuration parameters. Once these parameters are migrated from the
+    /// @c CfgMgr this method will have to be modified accordingly.
+    ///
+    /// @todo Implement reporting a summary of interfaces being used for
+    /// receiving and sending DHCP messages. This will be implemented with
+    /// ticket #3512.
+    ///
+    /// @param selection Is a bitfield which describes the parts of the
+    /// configuration to be returned.
+    ///
+    /// @return Summary of the configuration in the textual format.
+    std::string getConfigSummary(const uint32_t selection) const;
+
+    /// @brief Returns configuration sequence number.
+    uint32_t getSequence() const {
+        return (sequence_);
+    }
+
+    /// @brief Compares configuration sequence with other sequence.
+    ///
+    /// This method compares sequence numbers of two configurations for
+    /// equality. The sequence numbers are meant to be unique, so if
+    /// they are equal it means that they point to the same configuration.
+    ///
+    /// @param other Configuration which sequence number should be
+    /// compared with the sequence number of this configuration.
+    ///
+    /// @return true if sequence numbers are equal.
+    bool sequenceEquals(const SrvConfig& other);
+
+    /// @brief Returns logging specific configuration.
+    const LoggingInfoStorage& getLoggingInfo() const {
+        return (logging_info_);
+    }
+
+    /// @brief Sets logging specific configuration.
+    ///
+    /// @param logging_info New logging configuration.
+    void addLoggingInfo(const LoggingInfo& logging_info) {
+        logging_info_.push_back(logging_info);
+    }
+
+    /// @brief Returns object which represents selection of interfaces.
+    ///
+    /// This function returns a reference to the object which represents the
+    /// set of interfaces being used to receive DHCP traffic.
+    ///
+    /// @return Object representing selection of interfaces.
+    const CfgIface& getCfgIface() const {
+        return (cfg_iface_);
+    }
+
+    /// @brief Sets the object representing selection of interfaces.
+    ///
+    /// @param cfg_iface Object representing selection of interfaces.
+    void setCfgIface(const CfgIface& cfg_iface) {
+        cfg_iface_ = cfg_iface;
+    }
+
+    /// @brief Copies the currnet configuration to a new configuration.
+    ///
+    /// This method copies the parameters stored in the configuration to
+    /// an object passed as parameter. The configuration sequence is not
+    /// copied.
+    ///
+    /// @param [out] new_config An object to which the configuration will
+    /// be copied.
+    void copy(SrvConfig& new_config) const;
+
+    /// @brief Apply logging configuration to log4cplus.
+    void applyLoggingCfg() const;
+
+    /// @name Methods and operators used to compare configurations.
+    ///
+    //@{
+    ///
+    /// @brief Compares two objects for equality.
+    ///
+    /// It ignores the configuration sequence number when checking for
+    /// equality of objects.
+    ///
+    /// @param other An object to be compared with this object.
+    ///
+    /// @return true if two objects are equal, false otherwise.
+    bool equals(const SrvConfig& other) const;
+
+    /// @brief Compares two objects for inequality.
+    ///
+    /// It ignores the configuration sequence number when checking for
+    /// inequality of objects.
+    ///
+    /// @param other An object to be compared with this object.
+    ///
+    /// @return true if two objects are not equal, false otherwise.
+    bool nequals(const SrvConfig& other) const {
+        return (!equals(other));
+    }
+
+    /// @brief Equality operator.
+    ///
+    /// It ignores the configuration sequence number when checking for
+    /// equality of objects.
+    ///
+    /// @param other An object to be compared with this object.
+    ///
+    /// @return true if two objects are equal, false otherwise.
+    bool operator==(const SrvConfig& other) const {
+        return (equals(other));
+    }
+
+    /// @param other An object to be compared with this object.
+    ///
+    /// It ignores the configuration sequence number when checking for
+    /// inequality of objects.
+    ///
+    /// @param other An object to be compared with this object.
+    ///
+    /// @return true if two objects are not equal, false otherwise.
+    bool operator!=(const SrvConfig& other) const {
+        return (nequals(other));
+    }
+
+    //@}
+
+private:
+
+    /// @brief Sequence number identifying the configuration.
+    uint32_t sequence_;
+
+    /// @brief Logging specific information.
+    LoggingInfoStorage logging_info_;
+
+    /// @brief Interface configuration.
+    ///
+    /// Used to select interfaces on which the DHCP server will listen to
+    /// queries.
+    CfgIface cfg_iface_;
+
+};
+
+/// @name Pointers to the @c SrvConfig object.
+///
+//@{
+/// @brief Non-const pointer to the @c SrvConfig.
+typedef boost::shared_ptr<SrvConfig> SrvConfigPtr;
+
+/// @brief Const pointer to the @c SrvConfig.
+typedef boost::shared_ptr<const SrvConfig> ConstSrvConfigPtr;
+//@}
+
+} // namespace isc::dhcp
+} // namespace isc
+
+#endif // DHCPSRV_CONFIG_H

+ 2 - 1
src/lib/dhcpsrv/tests/Makefile.am

@@ -55,7 +55,6 @@ libdhcpsrv_unittests_SOURCES  = run_unittests.cc
 libdhcpsrv_unittests_SOURCES += addr_utilities_unittest.cc
 libdhcpsrv_unittests_SOURCES += addr_utilities_unittest.cc
 libdhcpsrv_unittests_SOURCES += alloc_engine_unittest.cc
 libdhcpsrv_unittests_SOURCES += alloc_engine_unittest.cc
 libdhcpsrv_unittests_SOURCES += callout_handle_store_unittest.cc
 libdhcpsrv_unittests_SOURCES += callout_handle_store_unittest.cc
-libdhcpsrv_unittests_SOURCES += configuration_unittest.cc
 libdhcpsrv_unittests_SOURCES += cfgmgr_unittest.cc
 libdhcpsrv_unittests_SOURCES += cfgmgr_unittest.cc
 libdhcpsrv_unittests_SOURCES += csv_lease_file4_unittest.cc
 libdhcpsrv_unittests_SOURCES += csv_lease_file4_unittest.cc
 libdhcpsrv_unittests_SOURCES += csv_lease_file6_unittest.cc
 libdhcpsrv_unittests_SOURCES += csv_lease_file6_unittest.cc
@@ -69,6 +68,7 @@ libdhcpsrv_unittests_SOURCES += lease_unittest.cc
 libdhcpsrv_unittests_SOURCES += lease_mgr_factory_unittest.cc
 libdhcpsrv_unittests_SOURCES += lease_mgr_factory_unittest.cc
 libdhcpsrv_unittests_SOURCES += lease_mgr_unittest.cc
 libdhcpsrv_unittests_SOURCES += lease_mgr_unittest.cc
 libdhcpsrv_unittests_SOURCES += logging_unittest.cc
 libdhcpsrv_unittests_SOURCES += logging_unittest.cc
+libdhcpsrv_unittests_SOURCES += logging_info_unittest.cc
 libdhcpsrv_unittests_SOURCES += generic_lease_mgr_unittest.cc generic_lease_mgr_unittest.h
 libdhcpsrv_unittests_SOURCES += generic_lease_mgr_unittest.cc generic_lease_mgr_unittest.h
 libdhcpsrv_unittests_SOURCES += memfile_lease_mgr_unittest.cc
 libdhcpsrv_unittests_SOURCES += memfile_lease_mgr_unittest.cc
 libdhcpsrv_unittests_SOURCES += dhcp_parsers_unittest.cc
 libdhcpsrv_unittests_SOURCES += dhcp_parsers_unittest.cc
@@ -81,6 +81,7 @@ endif
 libdhcpsrv_unittests_SOURCES += pool_unittest.cc
 libdhcpsrv_unittests_SOURCES += pool_unittest.cc
 libdhcpsrv_unittests_SOURCES += schema_mysql_copy.h
 libdhcpsrv_unittests_SOURCES += schema_mysql_copy.h
 libdhcpsrv_unittests_SOURCES += schema_pgsql_copy.h
 libdhcpsrv_unittests_SOURCES += schema_pgsql_copy.h
+libdhcpsrv_unittests_SOURCES += srv_config_unittest.cc
 libdhcpsrv_unittests_SOURCES += subnet_unittest.cc
 libdhcpsrv_unittests_SOURCES += subnet_unittest.cc
 libdhcpsrv_unittests_SOURCES += test_get_callout_handle.cc test_get_callout_handle.h
 libdhcpsrv_unittests_SOURCES += test_get_callout_handle.cc test_get_callout_handle.h
 libdhcpsrv_unittests_SOURCES += triplet_unittest.cc
 libdhcpsrv_unittests_SOURCES += triplet_unittest.cc

+ 94 - 43
src/lib/dhcpsrv/tests/cfg_iface_unittest.cc

@@ -65,13 +65,13 @@ CfgIfaceTest::unicastOpen(const std::string& iface_name) const {
 // This test checks that the interface names can be explicitly selected
 // This test checks that the interface names can be explicitly selected
 // by their names and IPv4 sockets are opened on these interfaces.
 // by their names and IPv4 sockets are opened on these interfaces.
 TEST_F(CfgIfaceTest, explicitNamesV4) {
 TEST_F(CfgIfaceTest, explicitNamesV4) {
-    CfgIface cfg(CfgIface::V4);
+    CfgIface cfg;
     // Specify valid interface names. There should be no error.
     // Specify valid interface names. There should be no error.
-    ASSERT_NO_THROW(cfg.use("eth0"));
-    ASSERT_NO_THROW(cfg.use("eth1"));
+    ASSERT_NO_THROW(cfg.use(AF_INET, "eth0"));
+    ASSERT_NO_THROW(cfg.use(AF_INET, "eth1"));
 
 
     // Open sockets on specified interfaces.
     // Open sockets on specified interfaces.
-    cfg.openSockets(DHCP4_SERVER_PORT);
+    cfg.openSockets(AF_INET, DHCP4_SERVER_PORT);
 
 
     // Sockets should be now open on eth0 and eth1, but not on loopback.
     // Sockets should be now open on eth0 and eth1, but not on loopback.
     EXPECT_TRUE(socketOpen("eth0", AF_INET));
     EXPECT_TRUE(socketOpen("eth0", AF_INET));
@@ -91,9 +91,9 @@ TEST_F(CfgIfaceTest, explicitNamesV4) {
 
 
     // Reset configuration and select only one interface this time.
     // Reset configuration and select only one interface this time.
     cfg.reset();
     cfg.reset();
-    ASSERT_NO_THROW(cfg.use("eth1"));
+    ASSERT_NO_THROW(cfg.use(AF_INET, "eth1"));
 
 
-    cfg.openSockets(DHCP4_SERVER_PORT);
+    cfg.openSockets(AF_INET, DHCP4_SERVER_PORT);
 
 
     // Socket should be open on eth1 only.
     // Socket should be open on eth1 only.
     EXPECT_FALSE(socketOpen("eth0", AF_INET));
     EXPECT_FALSE(socketOpen("eth0", AF_INET));
@@ -105,13 +105,13 @@ TEST_F(CfgIfaceTest, explicitNamesV4) {
 // This test checks that the interface names can be explicitly selected
 // This test checks that the interface names can be explicitly selected
 // by their names and IPv6 sockets are opened on these interfaces.
 // by their names and IPv6 sockets are opened on these interfaces.
 TEST_F(CfgIfaceTest, explicitNamesV6) {
 TEST_F(CfgIfaceTest, explicitNamesV6) {
-    CfgIface cfg(CfgIface::V6);
+    CfgIface cfg;
     // Specify valid interface names. There should be no error.
     // Specify valid interface names. There should be no error.
-    ASSERT_NO_THROW(cfg.use("eth0"));
-    ASSERT_NO_THROW(cfg.use("eth1"));
+    ASSERT_NO_THROW(cfg.use(AF_INET6, "eth0"));
+    ASSERT_NO_THROW(cfg.use(AF_INET6, "eth1"));
 
 
     // Open sockets on specified interfaces.
     // Open sockets on specified interfaces.
-    cfg.openSockets(DHCP6_SERVER_PORT);
+    cfg.openSockets(AF_INET6, DHCP6_SERVER_PORT);
 
 
     // Sockets should be now open on eth0 and eth1, but not on loopback.
     // Sockets should be now open on eth0 and eth1, but not on loopback.
     EXPECT_TRUE(socketOpen("eth0", AF_INET6));
     EXPECT_TRUE(socketOpen("eth0", AF_INET6));
@@ -131,9 +131,9 @@ TEST_F(CfgIfaceTest, explicitNamesV6) {
 
 
     // Reset configuration and select only one interface this time.
     // Reset configuration and select only one interface this time.
     cfg.reset();
     cfg.reset();
-    ASSERT_NO_THROW(cfg.use("eth1"));
+    ASSERT_NO_THROW(cfg.use(AF_INET6, "eth1"));
 
 
-    cfg.openSockets(DHCP6_SERVER_PORT);
+    cfg.openSockets(AF_INET6, DHCP6_SERVER_PORT);
 
 
     // Socket should be open on eth1 only.
     // Socket should be open on eth1 only.
     EXPECT_FALSE(socketOpen("eth0", AF_INET6));
     EXPECT_FALSE(socketOpen("eth0", AF_INET6));
@@ -145,10 +145,10 @@ TEST_F(CfgIfaceTest, explicitNamesV6) {
 // This test checks that the wildcard interface name can be specified to
 // This test checks that the wildcard interface name can be specified to
 // select all interfaces to open IPv4 sockets.
 // select all interfaces to open IPv4 sockets.
 TEST_F(CfgIfaceTest, wildcardV4) {
 TEST_F(CfgIfaceTest, wildcardV4) {
-    CfgIface cfg(CfgIface::V4);
-    ASSERT_NO_THROW(cfg.use("*"));
+    CfgIface cfg;
+    ASSERT_NO_THROW(cfg.use(AF_INET, "*"));
 
 
-    cfg.openSockets(DHCP4_SERVER_PORT);
+    cfg.openSockets(AF_INET, DHCP4_SERVER_PORT);
 
 
     // Sockets should be now open on eth0 and eth1, but not on loopback.
     // Sockets should be now open on eth0 and eth1, but not on loopback.
     EXPECT_TRUE(socketOpen("eth0", AF_INET));
     EXPECT_TRUE(socketOpen("eth0", AF_INET));
@@ -164,10 +164,10 @@ TEST_F(CfgIfaceTest, wildcardV4) {
 // This test checks that the wildcard interface name can be specified to
 // This test checks that the wildcard interface name can be specified to
 // select all interfaces to open IPv6 sockets.
 // select all interfaces to open IPv6 sockets.
 TEST_F(CfgIfaceTest, wildcardV6) {
 TEST_F(CfgIfaceTest, wildcardV6) {
-    CfgIface cfg(CfgIface::V6);
-    ASSERT_NO_THROW(cfg.use("*"));
+    CfgIface cfg;
+    ASSERT_NO_THROW(cfg.use(AF_INET6, "*"));
 
 
-    cfg.openSockets(DHCP4_SERVER_PORT);
+    cfg.openSockets(AF_INET6, DHCP6_SERVER_PORT);
 
 
     // Sockets should be now open on eth0 and eth1, but not on loopback.
     // Sockets should be now open on eth0 and eth1, but not on loopback.
     EXPECT_TRUE(socketOpen("eth0", AF_INET6));
     EXPECT_TRUE(socketOpen("eth0", AF_INET6));
@@ -184,14 +184,14 @@ TEST_F(CfgIfaceTest, wildcardV6) {
 // the interface on which the socket bound to link local address is also
 // the interface on which the socket bound to link local address is also
 // opened.
 // opened.
 TEST_F(CfgIfaceTest, validUnicast) {
 TEST_F(CfgIfaceTest, validUnicast) {
-    CfgIface cfg(CfgIface::V6);
+    CfgIface cfg;
 
 
     // One socket will be opened on link-local address, one on unicast but
     // One socket will be opened on link-local address, one on unicast but
     // on the same interface.
     // on the same interface.
-    ASSERT_NO_THROW(cfg.use("eth0"));
-    ASSERT_NO_THROW(cfg.use("eth0/2001:db8:1::1"));
+    ASSERT_NO_THROW(cfg.use(AF_INET6, "eth0"));
+    ASSERT_NO_THROW(cfg.use(AF_INET6, "eth0/2001:db8:1::1"));
 
 
-    cfg.openSockets(DHCP6_SERVER_PORT);
+    cfg.openSockets(AF_INET6, DHCP6_SERVER_PORT);
 
 
     EXPECT_TRUE(socketOpen("eth0", AF_INET6));
     EXPECT_TRUE(socketOpen("eth0", AF_INET6));
     EXPECT_TRUE(unicastOpen("eth0"));
     EXPECT_TRUE(unicastOpen("eth0"));
@@ -199,28 +199,79 @@ TEST_F(CfgIfaceTest, validUnicast) {
 
 
 // Test that when invalid interface names are specified an exception is thrown.
 // Test that when invalid interface names are specified an exception is thrown.
 TEST_F(CfgIfaceTest, invalidValues) {
 TEST_F(CfgIfaceTest, invalidValues) {
-    CfgIface cfg(CfgIface::V4);
-    ASSERT_THROW(cfg.use(""), InvalidIfaceName);
-    ASSERT_THROW(cfg.use(" "), InvalidIfaceName);
-    ASSERT_THROW(cfg.use("bogus"), NoSuchIface);
-
-    ASSERT_NO_THROW(cfg.use("eth0"));
-    ASSERT_THROW(cfg.use("eth0"), DuplicateIfaceName);
-
-    ASSERT_THROW(cfg.use("eth0/2001:db8:1::1"), InvalidIfaceName);
-
-    cfg.setFamily(CfgIface::V6);
-
-    ASSERT_THROW(cfg.use("eth0/"), InvalidIfaceName);
-    ASSERT_THROW(cfg.use("/2001:db8:1::1"), InvalidIfaceName);
-    ASSERT_THROW(cfg.use("*/2001:db8:1::1"), InvalidIfaceName);
-    ASSERT_THROW(cfg.use("bogus/2001:db8:1::1"), NoSuchIface);
-    ASSERT_THROW(cfg.use("eth0/fe80::3a60:77ff:fed5:cdef"), InvalidIfaceName);
-    ASSERT_THROW(cfg.use("eth0/fe80::3a60:77ff:fed5:cdef"), InvalidIfaceName);
-    ASSERT_THROW(cfg.use("eth0/2001:db8:1::2"), NoSuchAddress);
+    CfgIface cfg;
+    ASSERT_THROW(cfg.use(AF_INET, ""), InvalidIfaceName);
+    ASSERT_THROW(cfg.use(AF_INET, " "), InvalidIfaceName);
+    ASSERT_THROW(cfg.use(AF_INET, "bogus"), NoSuchIface);
+
+    ASSERT_NO_THROW(cfg.use(AF_INET, "eth0"));
+    ASSERT_THROW(cfg.use(AF_INET, "eth0"), DuplicateIfaceName);
+
+    ASSERT_THROW(cfg.use(AF_INET, "eth0/2001:db8:1::1"), InvalidIfaceName);
+
+    ASSERT_THROW(cfg.use(AF_INET6, "eth0/"), InvalidIfaceName);
+    ASSERT_THROW(cfg.use(AF_INET6, "/2001:db8:1::1"), InvalidIfaceName);
+    ASSERT_THROW(cfg.use(AF_INET6, "*/2001:db8:1::1"), InvalidIfaceName);
+    ASSERT_THROW(cfg.use(AF_INET6, "bogus/2001:db8:1::1"), NoSuchIface);
+    ASSERT_THROW(cfg.use(AF_INET6, "eth0/fe80::3a60:77ff:fed5:cdef"),
+                 InvalidIfaceName);
+    ASSERT_THROW(cfg.use(AF_INET6, "eth0/fe80::3a60:77ff:fed5:cdef"),
+                 InvalidIfaceName);
+    ASSERT_THROW(cfg.use(AF_INET6, "eth0/2001:db8:1::2"), NoSuchAddress);
+    ASSERT_NO_THROW(cfg.use(AF_INET6, "*"));
+    ASSERT_THROW(cfg.use(AF_INET6, "*"), DuplicateIfaceName);
+}
 
 
-    ASSERT_NO_THROW(cfg.use("*"));
-    ASSERT_THROW(cfg.use("*"), DuplicateIfaceName);
+// Test that the equality and inequality operators work fine for CfgIface.
+TEST_F(CfgIfaceTest, equality) {
+    CfgIface cfg1;
+    CfgIface cfg2;
+
+    // Initially objects must be equal.
+    EXPECT_TRUE(cfg1 == cfg2);
+    EXPECT_FALSE(cfg1 != cfg2);
+
+    // Differ by one interface.
+    cfg1.use(AF_INET, "eth0");
+    EXPECT_FALSE(cfg1 == cfg2);
+    EXPECT_TRUE(cfg1 != cfg2);
+
+    // Now interfaces should be equal.
+    cfg2.use(AF_INET, "eth0");
+    EXPECT_TRUE(cfg1 == cfg2);
+    EXPECT_FALSE(cfg1 != cfg2);
+
+    // Differ by unicast address.
+    cfg1.use(AF_INET6, "eth0/2001:db8:1::1");
+    EXPECT_FALSE(cfg1 == cfg2);
+    EXPECT_TRUE(cfg1 != cfg2);
+
+    // Differ by unicast address and one interface.
+    cfg2.use(AF_INET6, "eth1");
+    EXPECT_FALSE(cfg1 == cfg2);
+    EXPECT_TRUE(cfg1 != cfg2);
+
+    // Now, the unicast addresses are equal but still differ by one interface.
+    cfg2.use(AF_INET6, "eth0/2001:db8:1::1");
+    EXPECT_FALSE(cfg1 == cfg2);
+    EXPECT_TRUE(cfg1 != cfg2);
+
+    // They should be now back to equal.
+    cfg1.use(AF_INET6, "eth1");
+    EXPECT_TRUE(cfg1 == cfg2);
+    EXPECT_FALSE(cfg1 != cfg2);
+
+    // Even though the wildcard doesn't change anything because all interfaces
+    // are already in use, the fact that the wildcard is specified should
+    // cause them to be not equal.
+    cfg1.use(AF_INET6, "*");
+    EXPECT_FALSE(cfg1 == cfg2);
+    EXPECT_TRUE(cfg1 != cfg2);
+
+    // Finally, both are equal as they use wildacard.
+    cfg2.use(AF_INET, "*");
+    EXPECT_TRUE(cfg1 == cfg2);
+    EXPECT_FALSE(cfg1 != cfg2);
 }
 }
 
 
 } // end of anonymous namespace
 } // end of anonymous namespace

+ 157 - 5
src/lib/dhcpsrv/tests/cfgmgr_unittest.cc

@@ -262,9 +262,7 @@ class CfgMgrTest : public ::testing::Test {
 public:
 public:
     CfgMgrTest() {
     CfgMgrTest() {
         // make sure we start with a clean configuration
         // make sure we start with a clean configuration
-        CfgMgr::instance().deleteSubnets4();
-        CfgMgr::instance().deleteSubnets6();
-        CfgMgr::instance().deleteOptionDefs();
+        clear();
     }
     }
 
 
     /// @brief generates interface-id option based on provided text
     /// @brief generates interface-id option based on provided text
@@ -279,9 +277,15 @@ public:
 
 
     ~CfgMgrTest() {
     ~CfgMgrTest() {
         // clean up after the test
         // clean up after the test
+        clear();
+    }
+
+    void clear() {
+        CfgMgr::instance().setVerbose(false);
         CfgMgr::instance().deleteSubnets4();
         CfgMgr::instance().deleteSubnets4();
         CfgMgr::instance().deleteSubnets6();
         CfgMgr::instance().deleteSubnets6();
         CfgMgr::instance().deleteOptionDefs();
         CfgMgr::instance().deleteOptionDefs();
+        CfgMgr::instance().clear();
     }
     }
 
 
     /// used in client classification (or just empty container for other tests)
     /// used in client classification (or just empty container for other tests)
@@ -292,10 +296,13 @@ public:
 // it is empty by default.
 // it is empty by default.
 TEST_F(CfgMgrTest, configuration) {
 TEST_F(CfgMgrTest, configuration) {
 
 
-    ConfigurationPtr configuration = CfgMgr::instance().getConfiguration();
+    ConstSrvConfigPtr configuration = CfgMgr::instance().getCurrentCfg();
     ASSERT_TRUE(configuration);
     ASSERT_TRUE(configuration);
+    EXPECT_TRUE(configuration->getLoggingInfo().empty());
 
 
-    EXPECT_TRUE(configuration->logging_info_.empty());
+    configuration = CfgMgr::instance().getStagingCfg();
+    ASSERT_TRUE(configuration);
+    EXPECT_TRUE(configuration->getLoggingInfo().empty());
 }
 }
 
 
 // This test verifies that multiple option definitions can be added
 // This test verifies that multiple option definitions can be added
@@ -1119,6 +1126,151 @@ TEST_F(CfgMgrTest, subnet6Duplication) {
 }
 }
 
 
 
 
+// This test verifies that the configuration staging, commit and rollback works
+// as expected.
+TEST_F(CfgMgrTest, staging) {
+    CfgMgr& cfg_mgr = CfgMgr::instance();
+    // Initially, the current configuration is a default one. We are going
+    // to get the current configuration a couple of times and make sure
+    // that always the same instance is returned.
+    ConstSrvConfigPtr const_config;
+    for (int i = 0; i < 5; ++i) {
+        const_config = cfg_mgr.getCurrentCfg();
+        ASSERT_TRUE(const_config) << "Returned NULL current configuration"
+            " for iteration " << i;
+        EXPECT_EQ(0, const_config->getSequence())
+            << "Returned invalid sequence number "
+            << const_config->getSequence() << " for iteration " << i;
+    }
+
+    // Try to get the new staging configuration. When getStagingCfg() is called
+    // for the first time the new instance of the staging configuration is
+    // returned. This instance is returned for every call to getStagingCfg()
+    // until commit is called.
+    SrvConfigPtr config;
+    for (int i = 0; i < 5; ++i) {
+        config = cfg_mgr.getStagingCfg();
+        ASSERT_TRUE(config) << "Returned NULL staging configuration for"
+            " iteration " << i;
+        // The sequence id is 1 for staging because it is ahead of current
+        // configuration having sequence number 0.
+        EXPECT_EQ(1, config->getSequence()) << "Returned invalid sequence"
+            " number " << config->getSequence() << " for iteration " << i;
+    }
+
+    // This should change the staging configuration so as it becomes a current
+    // one.
+    cfg_mgr.commit();
+    const_config = cfg_mgr.getCurrentCfg();
+    ASSERT_TRUE(const_config);
+    // Sequence id equal to 1 indicates that the current configuration points
+    // to the configuration that used to be a staging configuration previously.
+    EXPECT_EQ(1, const_config->getSequence());
+
+    // Create a new staging configuration. It should be assigned a new
+    // sequence id.
+    config = cfg_mgr.getStagingCfg();
+    ASSERT_TRUE(config);
+    EXPECT_EQ(2, config->getSequence());
+
+    // Let's execute commit a couple of times. The first invocation to commit
+    // changes the configuration having sequence 2 to current configuration.
+    // Other commits are no-op.
+    for (int i = 0; i < 5; ++i) {
+        cfg_mgr.commit();
+    }
+
+    // The current configuration now have sequence number 2.
+    const_config = cfg_mgr.getCurrentCfg();
+    ASSERT_TRUE(const_config);
+    EXPECT_EQ(2, const_config->getSequence());
+
+    // Clear configuration along with a history.
+    cfg_mgr.clear();
+
+    // After clearing configuration we should successfully get the
+    // new staging configuration.
+    config = cfg_mgr.getStagingCfg();
+    ASSERT_TRUE(config);
+    EXPECT_EQ(1, config->getSequence());
+
+    // Modify the staging configuration.
+    config->addLoggingInfo(LoggingInfo());
+    ASSERT_TRUE(config);
+    // The modified staging configuration should have one logger configured.
+    ASSERT_EQ(1, config->getLoggingInfo().size());
+
+    // Rollback should remove a staging configuration, including the logger.
+    ASSERT_NO_THROW(cfg_mgr.rollback());
+
+    // Make sure that the logger is not set. This is an indication that the
+    // rollback worked.
+    config = cfg_mgr.getStagingCfg();
+    ASSERT_TRUE(config);
+    EXPECT_EQ(0, config->getLoggingInfo().size());
+}
+
+// This test verifies that it is possible to revert to an old configuration.
+TEST_F(CfgMgrTest, revert) {
+    CfgMgr& cfg_mgr = CfgMgr::instance();
+    // Let's create 5 unique configurations: differing by a debug level in the
+    // range of 10 to 14.
+    for (int i = 0; i < 5; ++i) {
+        SrvConfigPtr config = cfg_mgr.getStagingCfg();
+        LoggingInfo logging_info;
+        logging_info.debuglevel_ = i + 10;
+        config->addLoggingInfo(logging_info);
+        cfg_mgr.commit();
+    }
+
+    // Now we have 6 configurations with:
+    // - debuglevel = 99 (a default one)
+    // - debuglevel = 10
+    // - debuglevel = 11
+    // - debuglevel = 12
+    // - debuglevel = 13
+    // - debuglevel = 14 (current)
+
+    // Hence, the maximum index of the configuration to revert is 5 (which
+    // points to the configuration with debuglevel = 99). For the index greater
+    // than 5 we should get an exception.
+    ASSERT_THROW(cfg_mgr.revert(6), isc::OutOfRange);
+    // Value of 0 also doesn't make sense.
+    ASSERT_THROW(cfg_mgr.revert(0), isc::OutOfRange);
+
+    // We should be able to revert to configuration with debuglevel = 10.
+    ASSERT_NO_THROW(cfg_mgr.revert(4));
+    // And this configuration should be now the current one and the debuglevel
+    // of this configuration is 10.
+    EXPECT_EQ(10, cfg_mgr.getCurrentCfg()->getLoggingInfo()[0].debuglevel_);
+    EXPECT_NE(cfg_mgr.getCurrentCfg()->getSequence(), 1);
+
+    // The new set of configuration is now as follows:
+    // - debuglevel = 99
+    // - debuglevel = 10
+    // - debuglevel = 11
+    // - debuglevel = 12
+    // - debuglevel = 13
+    // - debuglevel = 14
+    // - debuglevel = 10 (current)
+    // So, reverting to configuration having index 3 means that the debug level
+    // of the current configuration will become 12.
+    ASSERT_NO_THROW(cfg_mgr.revert(3));
+    EXPECT_EQ(12, cfg_mgr.getCurrentCfg()->getLoggingInfo()[0].debuglevel_);
+}
+
+// This test verifies that the verbosity can be set and obtained from the
+// configuration manager.
+TEST_F(CfgMgrTest, verbosity) {
+    ASSERT_FALSE(CfgMgr::instance().isVerbose());
+
+    CfgMgr::instance().setVerbose(true);
+    ASSERT_TRUE(CfgMgr::instance().isVerbose());
+
+    CfgMgr::instance().setVerbose(false);
+    EXPECT_FALSE(CfgMgr::instance().isVerbose());
+}
+
 /// @todo Add unit-tests for testing:
 /// @todo Add unit-tests for testing:
 /// - addActiveIface() with invalid interface name
 /// - addActiveIface() with invalid interface name
 /// - addActiveIface() with the same interface twice
 /// - addActiveIface() with the same interface twice

+ 10 - 8
src/lib/dhcpsrv/tests/daemon_unittest.cc

@@ -16,6 +16,7 @@
 
 
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 #include <cc/data.h>
 #include <cc/data.h>
+#include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/daemon.h>
 #include <dhcpsrv/daemon.h>
 #include <dhcpsrv/logging.h>
 #include <dhcpsrv/logging.h>
 #include <log/logger_unittest_support.h>
 #include <log/logger_unittest_support.h>
@@ -68,9 +69,10 @@ TEST_F(DaemonTest, constructor) {
 // More dedicated tests are availablef for LogConfigParser class.
 // More dedicated tests are availablef for LogConfigParser class.
 // See logger_unittest.cc
 // See logger_unittest.cc
 TEST_F(DaemonTest, parsingConsoleOutput) {
 TEST_F(DaemonTest, parsingConsoleOutput) {
+    CfgMgr::instance().setVerbose(false);
 
 
     // Storage - parsed configuration will be stored here
     // Storage - parsed configuration will be stored here
-    ConfigurationPtr storage(new Configuration());
+    SrvConfigPtr storage(new SrvConfig());
 
 
     const char* config_txt =
     const char* config_txt =
     "{ \"loggers\": ["
     "{ \"loggers\": ["
@@ -89,18 +91,18 @@ TEST_F(DaemonTest, parsingConsoleOutput) {
 
 
     // Spawn a daemon and tell it to configure logger
     // Spawn a daemon and tell it to configure logger
     Daemon x;
     Daemon x;
-    EXPECT_NO_THROW(x.configureLogger(config, storage, false));
+    EXPECT_NO_THROW(x.configureLogger(config, storage));
 
 
     // The parsed configuration should be processed by the daemon and
     // The parsed configuration should be processed by the daemon and
     // stored in configuration storage.
     // stored in configuration storage.
-    ASSERT_EQ(1, storage->logging_info_.size());
+    ASSERT_EQ(1, storage->getLoggingInfo().size());
 
 
-    EXPECT_EQ("kea", storage->logging_info_[0].name_);
-    EXPECT_EQ(99, storage->logging_info_[0].debuglevel_);
-    EXPECT_EQ(isc::log::DEBUG, storage->logging_info_[0].severity_);
+    EXPECT_EQ("kea", storage->getLoggingInfo()[0].name_);
+    EXPECT_EQ(99, storage->getLoggingInfo()[0].debuglevel_);
+    EXPECT_EQ(isc::log::DEBUG, storage->getLoggingInfo()[0].severity_);
 
 
-    ASSERT_EQ(1, storage->logging_info_[0].destinations_.size());
-    EXPECT_EQ("stdout" , storage->logging_info_[0].destinations_[0].output_);
+    ASSERT_EQ(1, storage->getLoggingInfo()[0].destinations_.size());
+    EXPECT_EQ("stdout" , storage->getLoggingInfo()[0].destinations_[0].output_);
 }
 }
 
 
 
 

+ 14 - 10
src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc

@@ -59,8 +59,7 @@ public:
 
 
     /// @brief Resets selection of the interfaces from previous tests.
     /// @brief Resets selection of the interfaces from previous tests.
     void resetIfaceCfg() {
     void resetIfaceCfg() {
-        CfgMgr::instance().getConfiguration()->cfg_iface_.closeSockets();
-        CfgMgr::instance().getConfiguration()->cfg_iface_.reset();
+        CfgMgr::instance().clear();
     }
     }
 };
 };
 
 
@@ -225,11 +224,14 @@ TEST_F(DhcpParserTest, interfaceListParserTest) {
 
 
     const std::string name = "interfaces";
     const std::string name = "interfaces";
 
 
+    ParserContextPtr parser_context(new ParserContext(Option::V4));
+
     // Verify that parser constructor fails if parameter name isn't "interface"
     // Verify that parser constructor fails if parameter name isn't "interface"
-    EXPECT_THROW(InterfaceListConfigParser("bogus_name"), isc::BadValue);
+    EXPECT_THROW(InterfaceListConfigParser("bogus_name", parser_context),
+                 isc::BadValue);
 
 
     boost::scoped_ptr<InterfaceListConfigParser>
     boost::scoped_ptr<InterfaceListConfigParser>
-        parser(new InterfaceListConfigParser(name));
+        parser(new InterfaceListConfigParser(name, parser_context));
     ElementPtr list_element = Element::createList();
     ElementPtr list_element = Element::createList();
     list_element->add(Element::create("eth0"));
     list_element->add(Element::create("eth0"));
 
 
@@ -240,9 +242,9 @@ TEST_F(DhcpParserTest, interfaceListParserTest) {
 
 
     // Use CfgMgr instance to check if eth0 and eth1 was added, and that
     // Use CfgMgr instance to check if eth0 and eth1 was added, and that
     // eth2 was not added.
     // eth2 was not added.
-    ConfigurationPtr cfg = CfgMgr::instance().getConfiguration();
+    SrvConfigPtr cfg = CfgMgr::instance().getStagingCfg();
     ASSERT_TRUE(cfg);
     ASSERT_TRUE(cfg);
-    ASSERT_NO_THROW(cfg->cfg_iface_.openSockets(10000));
+    ASSERT_NO_THROW(cfg->getCfgIface().openSockets(AF_INET, 10000));
 
 
     EXPECT_TRUE(test_config.socketOpen("eth0", AF_INET));
     EXPECT_TRUE(test_config.socketOpen("eth0", AF_INET));
     EXPECT_FALSE(test_config.socketOpen("eth1", AF_INET));
     EXPECT_FALSE(test_config.socketOpen("eth1", AF_INET));
@@ -253,13 +255,15 @@ TEST_F(DhcpParserTest, interfaceListParserTest) {
     list_element->add(Element::create("*"));
     list_element->add(Element::create("*"));
 
 
     // Reset parser and configuration.
     // Reset parser and configuration.
-    parser.reset(new InterfaceListConfigParser(name));
-    cfg->cfg_iface_.closeSockets();
-    cfg->cfg_iface_.reset();
+    parser.reset(new InterfaceListConfigParser(name, parser_context));
+    cfg->getCfgIface().closeSockets();
+    CfgMgr::instance().clear();
 
 
     parser->build(list_element);
     parser->build(list_element);
     parser->commit();
     parser->commit();
-    ASSERT_NO_THROW(cfg->cfg_iface_.openSockets(10000));
+
+    cfg = CfgMgr::instance().getStagingCfg();
+    ASSERT_NO_THROW(cfg->getCfgIface().openSockets(AF_INET, 10000));
 
 
     EXPECT_TRUE(test_config.socketOpen("eth0", AF_INET));
     EXPECT_TRUE(test_config.socketOpen("eth0", AF_INET));
     EXPECT_TRUE(test_config.socketOpen("eth1", AF_INET));
     EXPECT_TRUE(test_config.socketOpen("eth1", AF_INET));

+ 160 - 0
src/lib/dhcpsrv/tests/logging_info_unittest.cc

@@ -0,0 +1,160 @@
+// Copyright (C) 2014 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
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/logging_info.h>
+#include <gtest/gtest.h>
+
+using namespace isc::dhcp;
+
+namespace {
+
+// Checks if two destinations can be compared for equality.
+TEST(LoggingDestintaion, equals) {
+    LoggingDestination dest1;
+    LoggingDestination dest2;
+
+    EXPECT_TRUE(dest1.equals(dest2));
+
+    dest1.output_ = "stderr";
+    EXPECT_FALSE(dest1.equals(dest2));
+
+    dest2.output_ = "stdout";
+    EXPECT_FALSE(dest1.equals(dest2));
+
+    dest2.output_ = "stderr";
+    EXPECT_TRUE(dest1.equals(dest2));
+
+    dest1.maxver_ = 10;
+    dest2.maxver_ = 5;
+    EXPECT_FALSE(dest1.equals(dest2));
+
+    dest2.maxver_ = 10;
+    EXPECT_TRUE(dest1.equals(dest2));
+
+    dest1.maxsize_ = 64;
+    dest2.maxsize_ = 32;
+    EXPECT_FALSE(dest1.equals(dest2));
+
+    dest1.maxsize_ = 32;
+    EXPECT_TRUE(dest1.equals(dest2));
+}
+
+/// @brief Test fixture class for testing @c LoggingInfo.
+class LoggingInfoTest : public ::testing::Test {
+public:
+
+    /// @brief Setup the test.
+    virtual void SetUp() {
+        CfgMgr::instance().setVerbose(false);
+    }
+
+    /// @brief Clear after the test.
+    virtual void TearDown() {
+        CfgMgr::instance().setVerbose(false);
+    }
+};
+
+// Checks if default logging configuration is correct.
+TEST_F(LoggingInfoTest, defaults) {
+    LoggingInfo info_non_verbose;
+    EXPECT_EQ("kea", info_non_verbose.name_);
+    EXPECT_EQ(isc::log::INFO, info_non_verbose.severity_);
+    EXPECT_EQ(0, info_non_verbose.debuglevel_);
+
+    ASSERT_EQ(1, info_non_verbose.destinations_.size());
+    EXPECT_EQ("stdout", info_non_verbose.destinations_[0].output_);
+
+    CfgMgr::instance().setVerbose(true);
+    LoggingInfo info_verbose;
+    EXPECT_EQ("kea", info_verbose.name_);
+    EXPECT_EQ(isc::log::DEBUG, info_verbose.severity_);
+    EXPECT_EQ(99, info_verbose.debuglevel_);
+
+    ASSERT_EQ(1, info_verbose.destinations_.size());
+    EXPECT_EQ("stdout", info_verbose.destinations_[0].output_);
+}
+
+// Checks if (in)equality operators work for LoggingInfo.
+TEST_F(LoggingInfoTest, equalityOperators) {
+    LoggingInfo info1;
+    LoggingInfo info2;
+
+    // Initially, both objects are the same.
+    EXPECT_TRUE(info1 == info2);
+
+    // Differ by name.
+    info1.name_ = "foo";
+    info2.name_ = "bar";
+    EXPECT_FALSE(info1 == info2);
+    EXPECT_TRUE(info1 != info2);
+
+    // Names equal.
+    info2.name_ = "foo";
+    EXPECT_TRUE(info1 == info2);
+    EXPECT_FALSE(info1 != info2);
+
+    // Differ by severity.
+    info1.severity_ = isc::log::DEBUG;
+    info2.severity_ = isc::log::INFO;
+    EXPECT_FALSE(info1 == info2);
+    EXPECT_TRUE(info1 != info2);
+
+    // Severities equal.
+    info2.severity_ = isc::log::DEBUG;
+    EXPECT_TRUE(info1 == info2);
+    EXPECT_FALSE(info1 != info2);
+
+    // Differ by debug level.
+    info1.debuglevel_ = 99;
+    info2.debuglevel_ = 1;
+    EXPECT_FALSE(info1 == info2);
+    EXPECT_TRUE(info1 != info2);
+
+    // Debug level equal.
+    info2.debuglevel_ = 99;
+    EXPECT_TRUE(info1 == info2);
+    EXPECT_FALSE(info1 != info2);
+
+    // Create two different desinations.
+    LoggingDestination dest1;
+    LoggingDestination dest2;
+    dest1.output_ = "foo";
+    dest2.output_ = "bar";
+
+    // Push destinations in some order to info1.
+    info1.destinations_.push_back(dest1);
+    info1.destinations_.push_back(dest2);
+
+    // Push in reverse order to info2. Order shouldn't matter.
+    info2.destinations_.push_back(dest2);
+    info2.destinations_.push_back(dest1);
+
+    EXPECT_TRUE(info1 == info2);
+    EXPECT_FALSE(info1 != info2);
+
+    // Change one of the destinations.
+    LoggingDestination dest3;
+    dest3.output_ = "foobar";
+
+    info2.destinations_[2] = dest3;
+
+    // The should now be unequal.
+    EXPECT_FALSE(info1 == info2);
+    EXPECT_TRUE(info1 != info2);
+
+}
+
+} // end of anonymous namespace

+ 36 - 36
src/lib/dhcpsrv/tests/logging_unittest.cc

@@ -49,10 +49,10 @@ class LoggingTest : public ::testing::Test {
 // Checks that contructor is able to process specified storage properly
 // Checks that contructor is able to process specified storage properly
 TEST_F(LoggingTest, constructor) {
 TEST_F(LoggingTest, constructor) {
 
 
-    ConfigurationPtr null_ptr;
+    SrvConfigPtr null_ptr;
     EXPECT_THROW(LogConfigParser parser(null_ptr), BadValue);
     EXPECT_THROW(LogConfigParser parser(null_ptr), BadValue);
 
 
-    ConfigurationPtr nonnull(new Configuration());
+    SrvConfigPtr nonnull(new SrvConfig());
 
 
     EXPECT_NO_THROW(LogConfigParser parser(nonnull));
     EXPECT_NO_THROW(LogConfigParser parser(nonnull));
 }
 }
@@ -76,7 +76,7 @@ TEST_F(LoggingTest, parsingConsoleOutput) {
     "    }"
     "    }"
     "]}";
     "]}";
 
 
-    ConfigurationPtr storage(new Configuration());
+    SrvConfigPtr storage(new SrvConfig());
 
 
     LogConfigParser parser(storage);
     LogConfigParser parser(storage);
 
 
@@ -88,14 +88,14 @@ TEST_F(LoggingTest, parsingConsoleOutput) {
 
 
     EXPECT_NO_THROW(parser.parseConfiguration(config));
     EXPECT_NO_THROW(parser.parseConfiguration(config));
 
 
-    ASSERT_EQ(1, storage->logging_info_.size());
+    ASSERT_EQ(1, storage->getLoggingInfo().size());
 
 
-    EXPECT_EQ("kea", storage->logging_info_[0].name_);
-    EXPECT_EQ(99, storage->logging_info_[0].debuglevel_);
-    EXPECT_EQ(isc::log::DEBUG, storage->logging_info_[0].severity_);
+    EXPECT_EQ("kea", storage->getLoggingInfo()[0].name_);
+    EXPECT_EQ(99, storage->getLoggingInfo()[0].debuglevel_);
+    EXPECT_EQ(isc::log::DEBUG, storage->getLoggingInfo()[0].severity_);
 
 
-    ASSERT_EQ(1, storage->logging_info_[0].destinations_.size());
-    EXPECT_EQ("stdout" , storage->logging_info_[0].destinations_[0].output_);
+    ASSERT_EQ(1, storage->getLoggingInfo()[0].destinations_.size());
+    EXPECT_EQ("stdout" , storage->getLoggingInfo()[0].destinations_[0].output_);
 }
 }
 
 
 // Checks if the LogConfigParser class is able to transform JSON structures
 // Checks if the LogConfigParser class is able to transform JSON structures
@@ -116,7 +116,7 @@ TEST_F(LoggingTest, parsingFile) {
     "    }"
     "    }"
     "]}";
     "]}";
 
 
-    ConfigurationPtr storage(new Configuration());
+    SrvConfigPtr storage(new SrvConfig());
 
 
     LogConfigParser parser(storage);
     LogConfigParser parser(storage);
 
 
@@ -128,14 +128,14 @@ TEST_F(LoggingTest, parsingFile) {
 
 
     EXPECT_NO_THROW(parser.parseConfiguration(config));
     EXPECT_NO_THROW(parser.parseConfiguration(config));
 
 
-    ASSERT_EQ(1, storage->logging_info_.size());
+    ASSERT_EQ(1, storage->getLoggingInfo().size());
 
 
-    EXPECT_EQ("kea", storage->logging_info_[0].name_);
-    EXPECT_EQ(0, storage->logging_info_[0].debuglevel_);
-    EXPECT_EQ(isc::log::INFO, storage->logging_info_[0].severity_);
+    EXPECT_EQ("kea", storage->getLoggingInfo()[0].name_);
+    EXPECT_EQ(0, storage->getLoggingInfo()[0].debuglevel_);
+    EXPECT_EQ(isc::log::INFO, storage->getLoggingInfo()[0].severity_);
 
 
-    ASSERT_EQ(1, storage->logging_info_[0].destinations_.size());
-    EXPECT_EQ("logfile.txt" , storage->logging_info_[0].destinations_[0].output_);
+    ASSERT_EQ(1, storage->getLoggingInfo()[0].destinations_.size());
+    EXPECT_EQ("logfile.txt" , storage->getLoggingInfo()[0].destinations_[0].output_);
 }
 }
 
 
 // Checks if the LogConfigParser class is able to transform data structures
 // Checks if the LogConfigParser class is able to transform data structures
@@ -166,7 +166,7 @@ TEST_F(LoggingTest, multipleLoggers) {
     "    }"
     "    }"
     "]}";
     "]}";
 
 
-    ConfigurationPtr storage(new Configuration());
+    SrvConfigPtr storage(new SrvConfig());
 
 
     LogConfigParser parser(storage);
     LogConfigParser parser(storage);
 
 
@@ -178,19 +178,19 @@ TEST_F(LoggingTest, multipleLoggers) {
 
 
     EXPECT_NO_THROW(parser.parseConfiguration(config));
     EXPECT_NO_THROW(parser.parseConfiguration(config));
 
 
-    ASSERT_EQ(2, storage->logging_info_.size());
+    ASSERT_EQ(2, storage->getLoggingInfo().size());
 
 
-    EXPECT_EQ("kea", storage->logging_info_[0].name_);
-    EXPECT_EQ(0, storage->logging_info_[0].debuglevel_);
-    EXPECT_EQ(isc::log::INFO, storage->logging_info_[0].severity_);
-    ASSERT_EQ(1, storage->logging_info_[0].destinations_.size());
-    EXPECT_EQ("logfile.txt" , storage->logging_info_[0].destinations_[0].output_);
+    EXPECT_EQ("kea", storage->getLoggingInfo()[0].name_);
+    EXPECT_EQ(0, storage->getLoggingInfo()[0].debuglevel_);
+    EXPECT_EQ(isc::log::INFO, storage->getLoggingInfo()[0].severity_);
+    ASSERT_EQ(1, storage->getLoggingInfo()[0].destinations_.size());
+    EXPECT_EQ("logfile.txt" , storage->getLoggingInfo()[0].destinations_[0].output_);
 
 
-    EXPECT_EQ("wombat", storage->logging_info_[1].name_);
-    EXPECT_EQ(99, storage->logging_info_[1].debuglevel_);
-    EXPECT_EQ(isc::log::DEBUG, storage->logging_info_[1].severity_);
-    ASSERT_EQ(1, storage->logging_info_[1].destinations_.size());
-    EXPECT_EQ("logfile2.txt" , storage->logging_info_[1].destinations_[0].output_);
+    EXPECT_EQ("wombat", storage->getLoggingInfo()[1].name_);
+    EXPECT_EQ(99, storage->getLoggingInfo()[1].debuglevel_);
+    EXPECT_EQ(isc::log::DEBUG, storage->getLoggingInfo()[1].severity_);
+    ASSERT_EQ(1, storage->getLoggingInfo()[1].destinations_.size());
+    EXPECT_EQ("logfile2.txt" , storage->getLoggingInfo()[1].destinations_[0].output_);
 }
 }
 
 
 // Checks if the LogConfigParser class is able to transform data structures
 // Checks if the LogConfigParser class is able to transform data structures
@@ -214,7 +214,7 @@ TEST_F(LoggingTest, multipleLoggingDestinations) {
     "    }"
     "    }"
     "]}";
     "]}";
 
 
-    ConfigurationPtr storage(new Configuration());
+    SrvConfigPtr storage(new SrvConfig());
 
 
     LogConfigParser parser(storage);
     LogConfigParser parser(storage);
 
 
@@ -226,14 +226,14 @@ TEST_F(LoggingTest, multipleLoggingDestinations) {
 
 
     EXPECT_NO_THROW(parser.parseConfiguration(config));
     EXPECT_NO_THROW(parser.parseConfiguration(config));
 
 
-    ASSERT_EQ(1, storage->logging_info_.size());
+    ASSERT_EQ(1, storage->getLoggingInfo().size());
 
 
-    EXPECT_EQ("kea", storage->logging_info_[0].name_);
-    EXPECT_EQ(0, storage->logging_info_[0].debuglevel_);
-    EXPECT_EQ(isc::log::INFO, storage->logging_info_[0].severity_);
-    ASSERT_EQ(2, storage->logging_info_[0].destinations_.size());
-    EXPECT_EQ("logfile.txt" , storage->logging_info_[0].destinations_[0].output_);
-    EXPECT_EQ("stdout" , storage->logging_info_[0].destinations_[1].output_);
+    EXPECT_EQ("kea", storage->getLoggingInfo()[0].name_);
+    EXPECT_EQ(0, storage->getLoggingInfo()[0].debuglevel_);
+    EXPECT_EQ(isc::log::INFO, storage->getLoggingInfo()[0].severity_);
+    ASSERT_EQ(2, storage->getLoggingInfo()[0].destinations_.size());
+    EXPECT_EQ("logfile.txt" , storage->getLoggingInfo()[0].destinations_[0].output_);
+    EXPECT_EQ("stdout" , storage->getLoggingInfo()[0].destinations_[1].output_);
 }
 }
 
 
 /// @todo There is no easy way to test applyConfiguration() and defaultLogging().
 /// @todo There is no easy way to test applyConfiguration() and defaultLogging().

+ 128 - 44
src/lib/dhcpsrv/tests/configuration_unittest.cc

@@ -14,17 +14,18 @@
 
 
 #include <config.h>
 #include <config.h>
 
 
+#include <dhcp/tests/iface_mgr_test_config.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/cfgmgr.h>
-#include <dhcpsrv/configuration.h>
+#include <dhcpsrv/srv_config.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/subnet.h>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
 using namespace isc::asiolink;
 using namespace isc::asiolink;
 using namespace isc::dhcp;
 using namespace isc::dhcp;
 
 
-// Those are the tests for Configuration storage. Right now they are minimal,
+// Those are the tests for SrvConfig storage. Right now they are minimal,
 // but the number is expected to grow significantly once we migrate more
 // but the number is expected to grow significantly once we migrate more
-// parameters from CfgMgr storage to Configuration storage.
+// parameters from CfgMgr storage to SrvConfig storage.
 
 
 namespace {
 namespace {
 
 
@@ -32,13 +33,14 @@ namespace {
 const int TEST_SUBNETS_NUM = 3;
 const int TEST_SUBNETS_NUM = 3;
 
 
 /// @brief Test fixture class for testing configuration data storage.
 /// @brief Test fixture class for testing configuration data storage.
-class ConfigurationTest : public ::testing::Test {
+class SrvConfigTest : public ::testing::Test {
 public:
 public:
     /// @brief Constructor.
     /// @brief Constructor.
     ///
     ///
     /// Creates IPv4 and IPv6 subnets for unit test. The number of subnets
     /// Creates IPv4 and IPv6 subnets for unit test. The number of subnets
     /// is @c TEST_SUBNETS_NUM for IPv4 and IPv6 each.
     /// is @c TEST_SUBNETS_NUM for IPv4 and IPv6 each.
-    ConfigurationTest() {
+    SrvConfigTest()
+        : iface_mgr_test_config_(true) {
         // Remove any subnets dangling from previous unit tests.
         // Remove any subnets dangling from previous unit tests.
         clearSubnets();
         clearSubnets();
 
 
@@ -74,7 +76,7 @@ public:
     /// @brief Destructor.
     /// @brief Destructor.
     ///
     ///
     /// Removes any dangling configuration.
     /// Removes any dangling configuration.
-    virtual ~ConfigurationTest() {
+    virtual ~SrvConfigTest() {
         clearSubnets();
         clearSubnets();
     }
     }
 
 
@@ -86,9 +88,9 @@ public:
     /// @c TEST_SUBNETS_NUM.
     /// @c TEST_SUBNETS_NUM.
     ///
     ///
     /// @todo Until the subnets configuration is migrated from the @c CfgMgr to
     /// @todo Until the subnets configuration is migrated from the @c CfgMgr to
-    /// the @c Configuration object, this function adds the subnet to the
+    /// the @c SrvConfig object, this function adds the subnet to the
     /// @c CfgMgr. Once, the subnet configuration is held in the
     /// @c CfgMgr. Once, the subnet configuration is held in the
-    /// @c Configuration this function must be modified to store the subnets in
+    /// @c SrvConfig this function must be modified to store the subnets in
     /// the @c conf_ object.
     /// the @c conf_ object.
     void addSubnet4(const unsigned int index);
     void addSubnet4(const unsigned int index);
 
 
@@ -100,16 +102,16 @@ public:
     /// @c TEST_SUBNETS_NUM.
     /// @c TEST_SUBNETS_NUM.
     ///
     ///
     /// @todo Until the subnets configuration is migrated from the @c CfgMgr to
     /// @todo Until the subnets configuration is migrated from the @c CfgMgr to
-    /// the @c Configuration object, this function adds the subnet to the
+    /// the @c SrvConfig object, this function adds the subnet to the
     /// @c CfgMgr. Once, the subnet configuration is held in the
     /// @c CfgMgr. Once, the subnet configuration is held in the
-    /// @c Configuration this function must be modified to store the subnets in
+    /// @c SrvConfig this function must be modified to store the subnets in
     /// @c conf_ object.
     /// @c conf_ object.
     void addSubnet6(const unsigned int index);
     void addSubnet6(const unsigned int index);
 
 
     /// @brief Removes all subnets from the configuration.
     /// @brief Removes all subnets from the configuration.
     ///
     ///
     /// @todo Modify this function once the subnet configuration is migrated
     /// @todo Modify this function once the subnet configuration is migrated
-    /// from @c CfgMgr to @c Configuration.
+    /// from @c CfgMgr to @c SrvConfig.
     void clearSubnets();
     void clearSubnets();
 
 
     /// @brief Enable/disable DDNS.
     /// @brief Enable/disable DDNS.
@@ -119,16 +121,18 @@ public:
     void enableDDNS(const bool enable);
     void enableDDNS(const bool enable);
 
 
     /// @brief Stores configuration.
     /// @brief Stores configuration.
-    Configuration conf_;
+    SrvConfig conf_;
     /// @brief A collection of IPv4 subnets used by unit tests.
     /// @brief A collection of IPv4 subnets used by unit tests.
     Subnet4Collection test_subnets4_;
     Subnet4Collection test_subnets4_;
     /// @brief A collection of IPv6 subnets used by unit tests.
     /// @brief A collection of IPv6 subnets used by unit tests.
     Subnet6Collection test_subnets6_;
     Subnet6Collection test_subnets6_;
+    /// @brief Fakes interface configuration.
+    isc::dhcp::test::IfaceMgrTestConfig iface_mgr_test_config_;
 
 
 };
 };
 
 
 void
 void
-ConfigurationTest::addSubnet4(const unsigned int index) {
+SrvConfigTest::addSubnet4(const unsigned int index) {
     if (index >= TEST_SUBNETS_NUM) {
     if (index >= TEST_SUBNETS_NUM) {
         FAIL() << "Subnet index " << index << "out of range (0.."
         FAIL() << "Subnet index " << index << "out of range (0.."
                << TEST_SUBNETS_NUM << "): " << "unable to add IPv4 subnet";
                << TEST_SUBNETS_NUM << "): " << "unable to add IPv4 subnet";
@@ -137,7 +141,7 @@ ConfigurationTest::addSubnet4(const unsigned int index) {
 }
 }
 
 
 void
 void
-ConfigurationTest::addSubnet6(const unsigned int index) {
+SrvConfigTest::addSubnet6(const unsigned int index) {
     if (index >= TEST_SUBNETS_NUM) {
     if (index >= TEST_SUBNETS_NUM) {
         FAIL() << "Subnet index " << index << "out of range (0.."
         FAIL() << "Subnet index " << index << "out of range (0.."
                << TEST_SUBNETS_NUM << "): " << "unable to add IPv6 subnet";
                << TEST_SUBNETS_NUM << "): " << "unable to add IPv6 subnet";
@@ -146,25 +150,26 @@ ConfigurationTest::addSubnet6(const unsigned int index) {
 }
 }
 
 
 void
 void
-ConfigurationTest::clearSubnets() {
+SrvConfigTest::clearSubnets() {
     CfgMgr::instance().deleteSubnets4();
     CfgMgr::instance().deleteSubnets4();
     CfgMgr::instance().deleteSubnets6();
     CfgMgr::instance().deleteSubnets6();
 }
 }
 
 
 void
 void
-ConfigurationTest::enableDDNS(const bool enable) {
+SrvConfigTest::enableDDNS(const bool enable) {
     // D2 configuration should always be non-NULL.
     // D2 configuration should always be non-NULL.
     CfgMgr::instance().getD2ClientConfig()->enableUpdates(enable);
     CfgMgr::instance().getD2ClientConfig()->enableUpdates(enable);
 }
 }
 
 
 // Check that by default there are no logging entries
 // Check that by default there are no logging entries
-TEST_F(ConfigurationTest, basic) {
-    EXPECT_TRUE(conf_.logging_info_.empty());
+TEST_F(SrvConfigTest, basic) {
+    EXPECT_TRUE(conf_.getLoggingInfo().empty());
 }
 }
 
 
-// Check that Configuration can store logging information.
-TEST_F(ConfigurationTest, loggingInfo) {
+// Check that SrvConfig can store logging information.
+TEST_F(SrvConfigTest, loggingInfo) {
     LoggingInfo log1;
     LoggingInfo log1;
+    log1.clearDestinations();
     log1.name_ = "foo";
     log1.name_ = "foo";
     log1.severity_ = isc::log::WARN;
     log1.severity_ = isc::log::WARN;
     log1.debuglevel_ = 77;
     log1.debuglevel_ = 77;
@@ -176,84 +181,163 @@ TEST_F(ConfigurationTest, loggingInfo) {
 
 
     log1.destinations_.push_back(dest);
     log1.destinations_.push_back(dest);
 
 
-    conf_.logging_info_.push_back(log1);
+    conf_.addLoggingInfo(log1);
 
 
-    EXPECT_EQ("foo", conf_.logging_info_[0].name_);
-    EXPECT_EQ(isc::log::WARN, conf_.logging_info_[0].severity_);
-    EXPECT_EQ(77, conf_.logging_info_[0].debuglevel_);
+    EXPECT_EQ("foo", conf_.getLoggingInfo()[0].name_);
+    EXPECT_EQ(isc::log::WARN, conf_.getLoggingInfo()[0].severity_);
+    EXPECT_EQ(77, conf_.getLoggingInfo()[0].debuglevel_);
 
 
-    EXPECT_EQ("some-logfile.txt", conf_.logging_info_[0].destinations_[0].output_);
-    EXPECT_EQ(5, conf_.logging_info_[0].destinations_[0].maxver_);
-    EXPECT_EQ(2097152, conf_.logging_info_[0].destinations_[0].maxsize_);
+    EXPECT_EQ("some-logfile.txt", conf_.getLoggingInfo()[0].destinations_[0].output_);
+    EXPECT_EQ(5, conf_.getLoggingInfo()[0].destinations_[0].maxver_);
+    EXPECT_EQ(2097152, conf_.getLoggingInfo()[0].destinations_[0].maxsize_);
 }
 }
 
 
 // Check that the configuration summary including information about the status
 // Check that the configuration summary including information about the status
 // of DDNS is returned.
 // of DDNS is returned.
-TEST_F(ConfigurationTest, summaryDDNS) {
+TEST_F(SrvConfigTest, summaryDDNS) {
     EXPECT_EQ("DDNS: disabled",
     EXPECT_EQ("DDNS: disabled",
-              conf_.getConfigSummary(Configuration::CFGSEL_DDNS));
+              conf_.getConfigSummary(SrvConfig::CFGSEL_DDNS));
 
 
     enableDDNS(true);
     enableDDNS(true);
     EXPECT_EQ("DDNS: enabled",
     EXPECT_EQ("DDNS: enabled",
-              conf_.getConfigSummary(Configuration::CFGSEL_DDNS));
+              conf_.getConfigSummary(SrvConfig::CFGSEL_DDNS));
 
 
     enableDDNS(false);
     enableDDNS(false);
     EXPECT_EQ("no IPv4 subnets!; no IPv6 subnets!; DDNS: disabled",
     EXPECT_EQ("no IPv4 subnets!; no IPv6 subnets!; DDNS: disabled",
-              conf_.getConfigSummary(Configuration::CFGSEL_ALL));
+              conf_.getConfigSummary(SrvConfig::CFGSEL_ALL));
 }
 }
 
 
 // Check that the configuration summary including information about added
 // Check that the configuration summary including information about added
 // subnets is returned.
 // subnets is returned.
-TEST_F(ConfigurationTest, summarySubnets) {
+TEST_F(SrvConfigTest, summarySubnets) {
     EXPECT_EQ("no config details available",
     EXPECT_EQ("no config details available",
-              conf_.getConfigSummary(Configuration::CFGSEL_NONE));
+              conf_.getConfigSummary(SrvConfig::CFGSEL_NONE));
 
 
     // Initially, there are no subnets added but it should be explicitly
     // Initially, there are no subnets added but it should be explicitly
     // reported when we query for information about the subnets.
     // reported when we query for information about the subnets.
     EXPECT_EQ("no IPv4 subnets!; no IPv6 subnets!",
     EXPECT_EQ("no IPv4 subnets!; no IPv6 subnets!",
-              conf_.getConfigSummary(Configuration::CFGSEL_SUBNET));
+              conf_.getConfigSummary(SrvConfig::CFGSEL_SUBNET));
 
 
     // If we just want information about IPv4 subnets, there should be no
     // If we just want information about IPv4 subnets, there should be no
     // mention of IPv6 subnets, even though there are none added.
     // mention of IPv6 subnets, even though there are none added.
     EXPECT_EQ("no IPv4 subnets!",
     EXPECT_EQ("no IPv4 subnets!",
-              conf_.getConfigSummary(Configuration::CFGSEL_SUBNET4));
+              conf_.getConfigSummary(SrvConfig::CFGSEL_SUBNET4));
 
 
     // If we just want information about IPv6 subnets, there should be no
     // If we just want information about IPv6 subnets, there should be no
     // mention of IPv4 subnets, even though there are none added.
     // mention of IPv4 subnets, even though there are none added.
     EXPECT_EQ("no IPv6 subnets!",
     EXPECT_EQ("no IPv6 subnets!",
-              conf_.getConfigSummary(Configuration::CFGSEL_SUBNET6));
+              conf_.getConfigSummary(SrvConfig::CFGSEL_SUBNET6));
 
 
     // Add IPv4 subnet and make sure it is reported.
     // Add IPv4 subnet and make sure it is reported.
     addSubnet4(0);
     addSubnet4(0);
     EXPECT_EQ("added IPv4 subnets: 1",
     EXPECT_EQ("added IPv4 subnets: 1",
-              conf_.getConfigSummary(Configuration::CFGSEL_SUBNET4));
+              conf_.getConfigSummary(SrvConfig::CFGSEL_SUBNET4));
     EXPECT_EQ("added IPv4 subnets: 1; no IPv6 subnets!",
     EXPECT_EQ("added IPv4 subnets: 1; no IPv6 subnets!",
-              conf_.getConfigSummary(Configuration::CFGSEL_SUBNET));
+              conf_.getConfigSummary(SrvConfig::CFGSEL_SUBNET));
 
 
     // Add IPv6 subnet and make sure it is reported.
     // Add IPv6 subnet and make sure it is reported.
     addSubnet6(0);
     addSubnet6(0);
     EXPECT_EQ("added IPv6 subnets: 1",
     EXPECT_EQ("added IPv6 subnets: 1",
-              conf_.getConfigSummary(Configuration::CFGSEL_SUBNET6));
+              conf_.getConfigSummary(SrvConfig::CFGSEL_SUBNET6));
     EXPECT_EQ("added IPv4 subnets: 1; added IPv6 subnets: 1",
     EXPECT_EQ("added IPv4 subnets: 1; added IPv6 subnets: 1",
-              conf_.getConfigSummary(Configuration::CFGSEL_SUBNET));
+              conf_.getConfigSummary(SrvConfig::CFGSEL_SUBNET));
 
 
     // Add one more subnet and make sure the bumped value is only
     // Add one more subnet and make sure the bumped value is only
     // for IPv4, but not for IPv6.
     // for IPv4, but not for IPv6.
     addSubnet4(1);
     addSubnet4(1);
     EXPECT_EQ("added IPv4 subnets: 2; added IPv6 subnets: 1",
     EXPECT_EQ("added IPv4 subnets: 2; added IPv6 subnets: 1",
-              conf_.getConfigSummary(Configuration::CFGSEL_SUBNET));
+              conf_.getConfigSummary(SrvConfig::CFGSEL_SUBNET));
     EXPECT_EQ("added IPv4 subnets: 2",
     EXPECT_EQ("added IPv4 subnets: 2",
-              conf_.getConfigSummary(Configuration::CFGSEL_SUBNET4));
+              conf_.getConfigSummary(SrvConfig::CFGSEL_SUBNET4));
 
 
     addSubnet6(1);
     addSubnet6(1);
     EXPECT_EQ("added IPv4 subnets: 2; added IPv6 subnets: 2",
     EXPECT_EQ("added IPv4 subnets: 2; added IPv6 subnets: 2",
-              conf_.getConfigSummary(Configuration::CFGSEL_SUBNET));
+              conf_.getConfigSummary(SrvConfig::CFGSEL_SUBNET));
 
 
     // Remove all subnets and make sure that there are no reported subnets
     // Remove all subnets and make sure that there are no reported subnets
     // back again.
     // back again.
     clearSubnets();
     clearSubnets();
     EXPECT_EQ("no IPv4 subnets!; no IPv6 subnets!",
     EXPECT_EQ("no IPv4 subnets!; no IPv6 subnets!",
-              conf_.getConfigSummary(Configuration::CFGSEL_SUBNET));
+              conf_.getConfigSummary(SrvConfig::CFGSEL_SUBNET));
+}
+
+// This test checks if entire configuration can be copied and that the sequence
+// number is not affected.
+TEST_F(SrvConfigTest, copy) {
+    // Create two configurations with different sequence numbers.
+    SrvConfig conf1(32);
+    SrvConfig conf2(64);
+
+    // Set logging information for conf1.
+    LoggingInfo info;
+    info.name_ = "foo";
+    info.severity_ = isc::log::DEBUG;
+    info.debuglevel_ = 64;
+    info.destinations_.push_back(LoggingDestination());
+
+    // Set interface configuration for conf1.
+    CfgIface cfg_iface;
+    cfg_iface.use(AF_INET, "eth0");
+
+    conf1.addLoggingInfo(info);
+    conf1.setCfgIface(cfg_iface);
+
+    // Make sure both configurations are different.
+    ASSERT_TRUE(conf1 != conf2);
+
+    // Copy conf1 to conf2.
+    ASSERT_NO_THROW(conf1.copy(conf2));
+
+    // Now they should be equal.
+    EXPECT_TRUE(conf1 == conf2);
+
+    // But, their sequence numbers should be unequal.
+    EXPECT_FALSE(conf1.sequenceEquals(conf2));
+}
+
+// This test checks that two configurations can be compared for (in)equality.
+TEST_F(SrvConfigTest, equality) {
+    SrvConfig conf1(32);
+    SrvConfig conf2(64);
+
+    // Initially, both objects should be equal, even though the configuration
+    // sequences are not matching.
+    EXPECT_TRUE(conf1 == conf2);
+    EXPECT_FALSE(conf1 != conf2);
+
+    // Differ by logging information.
+    LoggingInfo info1;
+    LoggingInfo info2;
+    info1.name_ = "foo";
+    info2.name_ = "bar";
+
+    conf1.addLoggingInfo(info1);
+    conf2.addLoggingInfo(info2);
+
+    EXPECT_FALSE(conf1 == conf2);
+    EXPECT_TRUE(conf1 != conf2);
+
+    conf1.addLoggingInfo(info2);
+    conf2.addLoggingInfo(info1);
+
+    EXPECT_TRUE(conf1 == conf2);
+    EXPECT_FALSE(conf1 != conf2);
+
+    // Differ by interface configuration.
+    CfgIface cfg_iface1;
+    CfgIface cfg_iface2;
+
+    cfg_iface1.use(AF_INET, "eth0");
+    conf1.setCfgIface(cfg_iface1);
+
+    EXPECT_FALSE(conf1 == conf2);
+    EXPECT_TRUE(conf1 != conf2);
+
+    cfg_iface2.use(AF_INET, "eth0");
+    conf2.setCfgIface(cfg_iface2);
+
+    EXPECT_TRUE(conf1 == conf2);
+    EXPECT_FALSE(conf1 != conf2);
 }
 }
 
 
 } // end of anonymous namespace
 } // end of anonymous namespace

+ 1 - 0
tools/.gitignore

@@ -0,0 +1 @@
+/path_replacer.sh