-// Copyright (C) 2012-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2019 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
configuration_->removeStatistics();
}
configs_.clear();
+ external_configs_.clear();
ensureCurrentAllocated();
}
return (configs_.back());
}
+SrvConfigPtr
+CfgMgr::createExternalCfg() {
+ uint32_t seq = 0;
+
+ if (!external_configs_.empty()) {
+ seq = external_configs_.rbegin()->second->getSequence() + 1;
+ }
+
+ SrvConfigPtr srv_config(new SrvConfig(seq));
+ external_configs_[seq] = srv_config;
+ return (srv_config);
+}
+
+void
+CfgMgr::mergeIntoStagingCfg(const uint32_t seq) {
+ mergeIntoCfg(getStagingCfg(), seq);
+}
+
+void
+CfgMgr::mergeIntoCurrentCfg(const uint32_t seq) {
+ mergeIntoCfg(getCurrentCfg(), seq);
+}
+
+void
+CfgMgr::mergeIntoCfg(const SrvConfigPtr& target_config, const uint32_t seq) {
+ auto source_config = external_configs_.find(seq);
+ if (source_config != external_configs_.end()) {
+ target_config->merge(*source_config->second);
+ external_configs_.erase(source_config);
+
+ } else {
+ isc_throw(BadValue, "the external configuration with the sequence number "
+ "of " << seq << " was not found");
+ }
+}
+
CfgMgr::CfgMgr()
: datadir_(DHCP_DATA_DIR), d2_client_mgr_(), family_(AF_INET) {
// DHCP_DATA_DIR must be set set with -DDHCP_DATA_DIR="..." in Makefile.am
-// Copyright (C) 2012-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2019 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
/// @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 removes all configurations, including current,
+ /// staging and external configurations. It creates a new current
+ /// configuration with default settings.
///
/// This function is exception safe.
void clear();
/// @return non-null pointer to the staging configuration.
SrvConfigPtr getStagingCfg();
+ /// @brief Creates external configuration and returns pointer to it.
+ ///
+ /// External configurations are those that come from other sources than
+ /// from the configuration file, e.g. a database or a command. They
+ /// are created aside and merged into the staging or current configuration.
+ /// External configurations are accessed by their sequence numbers. The
+ /// sequence numbers are autogenerated when the external configuration
+ /// instance is created.
+ ///
+ /// @return non-null pointer to created external configuration.
+ SrvConfigPtr createExternalCfg();
+
+ /// @brief Merges external configuration with the given sequence number
+ /// into the staging configuration.
+ ///
+ /// After the merge, the source configuration is discarded from the
+ /// @c CfgMgr as it should not be used anymore.
+ ///
+ /// @param seq Source configuration sequence number.
+ ///
+ /// @throw BadValue if the external configuration with the given sequence
+ /// number doesn't exist.
+ void mergeIntoStagingCfg(const uint32_t seq);
+
+ /// @brief Merges external configuration with the given sequence number
+ /// into the current configuration.
+ ///
+ /// After the merge, the source configuration is discarded from the
+ /// @c CfgMgr as it should not be used anymore.
+ ///
+ /// @param seq Source configuration sequence number.
+ ///
+ /// @throw BadValue if the external configuration with the given sequence
+ /// number doesn't exist.
+ void mergeIntoCurrentCfg(const uint32_t seq);
+
//@}
/// @brief Sets address family (AF_INET or AF_INET6)
/// default current configuration.
void ensureCurrentAllocated();
+
+ /// @brief Merges external configuration with the given sequence number
+ /// into the specified configuration.
+ ///
+ /// @param target_config Pointer to the configuration into which the
+ /// external configuration should be merged.
+ /// @param seq Source configuration sequence number.
+ void mergeIntoCfg(const SrvConfigPtr& taget_config, const uint32_t seq);
+
/// @brief directory where data files (e.g. server-id) are stored
std::string datadir_;
SrvConfigList configs_;
//@}
+ /// @name Map of external configurations.
+ ///
+ //@{
+ /// @brief Server configuration map type.
+ typedef std::map<uint32_t, SrvConfigPtr> SrvConfigMap;
+
+ /// @brief Map of external configurations with sequence numbers used
+ /// as keys.
+ SrvConfigMap external_configs_;
+ //@}
+
/// @brief Address family.
uint16_t family_;
};
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
+#include <exceptions/exceptions.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/srv_config.h>
#include <dhcpsrv/lease_mgr_factory.h>
ConfigBase::merge(other);
try {
+ /// @todo merge other parts of the configuration here.
+
const SrvConfig& other_srv_config = dynamic_cast<const SrvConfig&>(other);
+ cfg_subnets4_->merge(*other_srv_config.getCfgSubnets4());
+
+ /// @todo merge other parts of the configuration here.
} catch (const std::bad_cast&) {
+ isc_throw(InvalidOperation, "internal server error: must use derivation"
+ " of the SrvConfig as an argument of the call to"
+ " SrvConfig::merge()");
}
}
return (!equals(other));
}
- virtual void merge(const ConfigBase& other);
-
/// @brief Equality operator.
///
/// It ignores the configuration sequence number when checking for
//@}
+ /// @brief Merges the configuration specified as a parameter into
+ /// this configuration.
+ ///
+ /// This method is used when two or more configurations held in the
+ /// @c SrvConfig objects need to be combined into a single configuration.
+ /// Specifically, when the configuration backend is used, the part of
+ /// the server configuration comes from the configuration file and
+ /// stored in the staging configuration. The other part of the
+ /// configuration comes from the database. The configuration fetched
+ /// from the database is stored in a separate @c SrvConfig instance
+ /// and then merged into the staging configuration prior to commiting
+ /// it.
+ ///
+ /// The merging strategy depends on the underlying data being merged.
+ /// For example: subnets are merged using the algorithm implemented
+ /// in the @c CfgSubnets4. Other data structures are merged using the
+ /// algorithms implemented in their respective configuration
+ /// containers.
+ ///
+ /// The general rule is that the configuration data from the @c other
+ /// object replaces configuration data held in this object instance.
+ /// The data that do not overlap between the two objects is simply
+ /// inserted into this configuration.
+ ///
+ /// @note The call to @c merge may modify the data in the @c other
+ /// object. Therefore, the caller must not rely on the data held
+ /// in the @c other object after the call to @c merge. Also, the
+ /// data held in @c other must not be modified after the call to
+ /// @c merge because it may affect the merged configuration.
+ ///
+ /// The @c other parameter must be a @c SrvConfig or its derivation.
+ ///
+ /// Currently, the following parts of the configuration are merged:
+ /// - IPv4 subnets
+ ///
+ /// @todo Add support for merging other configuration elements.
+ ///
+ /// @param other An object holding the configuration to be merged
+ /// into this configuration.
+ virtual void merge(const ConfigBase& other);
+
/// @brief Updates statistics.
///
/// This method calls appropriate methods in child objects that update
-// Copyright (C) 2012-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2019 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
EXPECT_FALSE(stats_mgr.getObservation("subnet[123].assigned-pds"));
}
+// This test verifies that the external configuration can be merged into
+// the staging configuration via CfgMgr.
+TEST_F(CfgMgrTest, mergeIntoStagingCfg) {
+ CfgMgr& cfg_mgr = CfgMgr::instance();
+
+ // Create first external configuration.
+ SrvConfigPtr ext_cfg1;
+ ASSERT_NO_THROW(ext_cfg1 = cfg_mgr.createExternalCfg());
+ ASSERT_TRUE(ext_cfg1);
+ // It should pick the first available sequence number.
+ EXPECT_EQ(0, ext_cfg1->getSequence());
+
+ // Create second external configuration.
+ SrvConfigPtr ext_cfg2;
+ ASSERT_NO_THROW(ext_cfg2 = cfg_mgr.createExternalCfg());
+ ASSERT_TRUE(ext_cfg2);
+ // It should pick the next available sequence number.
+ EXPECT_EQ(1, ext_cfg2->getSequence());
+
+ // Those must be two separate instances.
+ ASSERT_FALSE(ext_cfg1 == ext_cfg2);
+
+ // Add a subnet which will be merged from first configuration.
+ Subnet4Ptr subnet1(new Subnet4(IOAddress("192.1.2.0"), 24, 1, 2, 3, 123));
+ ext_cfg1->getCfgSubnets4()->add(subnet1);
+
+ // Add a subnet which will be merged from the second configuration.
+ Subnet4Ptr subnet2(new Subnet4(IOAddress("192.1.3.0"), 24, 1, 2, 3, 124));
+ ext_cfg2->getCfgSubnets4()->add(subnet2);
+
+ // Merge first configuration.
+ ASSERT_NO_THROW(cfg_mgr.mergeIntoStagingCfg(ext_cfg1->getSequence()));
+ // Second attempt should fail because the configuration is discarded after
+ // the merge.
+ ASSERT_THROW(cfg_mgr.mergeIntoStagingCfg(ext_cfg1->getSequence()), BadValue);
+
+ // Check that the subnet from first configuration has been merged but not
+ // from the second configuration.
+ ASSERT_TRUE(cfg_mgr.getStagingCfg()->getCfgSubnets4()->getBySubnetId(123));
+ ASSERT_FALSE(cfg_mgr.getStagingCfg()->getCfgSubnets4()->getBySubnetId(124));
+
+ // Create another configuration instance to check what sequence it would
+ // pick. It should pick the first available one.
+ SrvConfigPtr ext_cfg3;
+ ASSERT_NO_THROW(ext_cfg3 = cfg_mgr.createExternalCfg());
+ ASSERT_TRUE(ext_cfg3);
+ EXPECT_EQ(2, ext_cfg3->getSequence());
+
+ // Merge the second and third (empty) configuration.
+ ASSERT_NO_THROW(cfg_mgr.mergeIntoStagingCfg(ext_cfg2->getSequence()));
+ ASSERT_NO_THROW(cfg_mgr.mergeIntoStagingCfg(ext_cfg3->getSequence()));
+
+ // Make sure that both subnets have been merged.
+ ASSERT_TRUE(cfg_mgr.getStagingCfg()->getCfgSubnets4()->getBySubnetId(123));
+ ASSERT_TRUE(cfg_mgr.getStagingCfg()->getCfgSubnets4()->getBySubnetId(124));
+
+ // The next configuration instance should reset the sequence to 0 because
+ // there are no other configurations in CfgMgr.
+ SrvConfigPtr ext_cfg4;
+ ASSERT_NO_THROW(ext_cfg4 = cfg_mgr.createExternalCfg());
+ ASSERT_TRUE(ext_cfg4);
+ EXPECT_EQ(0, ext_cfg4->getSequence());
+
+ // Try to commit the staging configuration.
+ ASSERT_NO_THROW(cfg_mgr.commit());
+
+ // Make sure that both subnets are present in the current configuration.
+ EXPECT_TRUE(cfg_mgr.getCurrentCfg()->getCfgSubnets4()->getBySubnetId(123));
+ EXPECT_TRUE(cfg_mgr.getCurrentCfg()->getCfgSubnets4()->getBySubnetId(124));
+
+ // The staging configuration should not include them.
+ EXPECT_FALSE(cfg_mgr.getStagingCfg()->getCfgSubnets4()->getBySubnetId(123));
+ EXPECT_FALSE(cfg_mgr.getStagingCfg()->getCfgSubnets4()->getBySubnetId(124));
+}
+
+// This test verifies that the external configuration can be merged into
+// the current configuration via CfgMgr.
+TEST_F(CfgMgrTest, mergeIntoCurrentCfg) {
+ CfgMgr& cfg_mgr = CfgMgr::instance();
+
+ // Create first external configuration.
+ SrvConfigPtr ext_cfg1;
+ ASSERT_NO_THROW(ext_cfg1 = cfg_mgr.createExternalCfg());
+ ASSERT_TRUE(ext_cfg1);
+ // It should pick the first available sequence number.
+ EXPECT_EQ(0, ext_cfg1->getSequence());
+
+ // Create second external configuration.
+ SrvConfigPtr ext_cfg2;
+ ASSERT_NO_THROW(ext_cfg2 = cfg_mgr.createExternalCfg());
+ ASSERT_TRUE(ext_cfg2);
+ // It should pick the next available sequence number.
+ EXPECT_EQ(1, ext_cfg2->getSequence());
+
+ // Those must be two separate instances.
+ ASSERT_FALSE(ext_cfg1 == ext_cfg2);
+
+ // Add a subnet which will be merged from first configuration.
+ Subnet4Ptr subnet1(new Subnet4(IOAddress("192.1.2.0"), 24, 1, 2, 3, 123));
+ ext_cfg1->getCfgSubnets4()->add(subnet1);
+
+ // Add a subnet which will be merged from the second configuration.
+ Subnet4Ptr subnet2(new Subnet4(IOAddress("192.1.3.0"), 24, 1, 2, 3, 124));
+ ext_cfg2->getCfgSubnets4()->add(subnet2);
+
+ // Merge first configuration.
+ ASSERT_NO_THROW(cfg_mgr.mergeIntoCurrentCfg(ext_cfg1->getSequence()));
+ // Second attempt should fail because the configuration is discarded after
+ // the merge.
+ ASSERT_THROW(cfg_mgr.mergeIntoCurrentCfg(ext_cfg1->getSequence()), BadValue);
+
+ // Check that the subnet from first configuration has been merged but not
+ // from the second configuration.
+ ASSERT_TRUE(cfg_mgr.getCurrentCfg()->getCfgSubnets4()->getBySubnetId(123));
+ ASSERT_FALSE(cfg_mgr.getCurrentCfg()->getCfgSubnets4()->getBySubnetId(124));
+
+ // Create another configuration instance to check what sequence it would
+ // pick. It should pick the first available one.
+ SrvConfigPtr ext_cfg3;
+ ASSERT_NO_THROW(ext_cfg3 = cfg_mgr.createExternalCfg());
+ ASSERT_TRUE(ext_cfg3);
+ EXPECT_EQ(2, ext_cfg3->getSequence());
+
+ // Merge the second and third (empty) configuration.
+ ASSERT_NO_THROW(cfg_mgr.mergeIntoCurrentCfg(ext_cfg2->getSequence()));
+ ASSERT_NO_THROW(cfg_mgr.mergeIntoCurrentCfg(ext_cfg3->getSequence()));
+
+ // Make sure that both subnets have been merged.
+ ASSERT_TRUE(cfg_mgr.getCurrentCfg()->getCfgSubnets4()->getBySubnetId(123));
+ ASSERT_TRUE(cfg_mgr.getCurrentCfg()->getCfgSubnets4()->getBySubnetId(124));
+
+ // The next configuration instance should reset the sequence to 0 because
+ // there are no other configurations in CfgMgr.
+ SrvConfigPtr ext_cfg4;
+ ASSERT_NO_THROW(ext_cfg4 = cfg_mgr.createExternalCfg());
+ ASSERT_TRUE(ext_cfg4);
+ EXPECT_EQ(0, ext_cfg4->getSequence());
+}
+
/// @todo Add unit-tests for testing:
/// - addActiveIface() with invalid interface name
/// - addActiveIface() with the same interface twice
-// Copyright (C) 2014-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2019 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
namespace {
+/// @brief Derivation of the @c ConfigBase not being @c SrvConfig.
+///
+/// This is used to verify that the appropriate error is returned
+/// when other derivation of the @c ConfigBase than @c SrvConfig
+/// is used.
+class NonSrvConfig : public ConfigBase { };
+
/// @brief Number of IPv4 and IPv6 subnets to be created for a test.
const int TEST_SUBNETS_NUM = 3;
EXPECT_TRUE(info_elem->equals(*check));
}
+// Verifies that exception is thrown when instead of SrvConfig
+// another derivation of ConfigBase is used in the call to
+// merge.
+TEST_F(SrvConfigTest, mergeBadCast) {
+ SrvConfig srv_config;
+ NonSrvConfig non_srv_config;
+ ASSERT_THROW(srv_config.merge(non_srv_config), isc::InvalidOperation);
+}
+
} // end of anonymous namespace