diff options
Diffstat (limited to 'src/lib/dhcpsrv/cfgmgr.cc')
-rw-r--r-- | src/lib/dhcpsrv/cfgmgr.cc | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/src/lib/dhcpsrv/cfgmgr.cc b/src/lib/dhcpsrv/cfgmgr.cc new file mode 100644 index 0000000..c532404 --- /dev/null +++ b/src/lib/dhcpsrv/cfgmgr.cc @@ -0,0 +1,233 @@ +// Copyright (C) 2012-2021 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 +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> +#include <asiolink/io_address.h> +#include <dhcp/iface_mgr.h> +#include <dhcp/libdhcp++.h> +#include <dhcpsrv/cfgmgr.h> +#include <dhcpsrv/dhcpsrv_log.h> +#include <sstream> +#include <string> + +using namespace isc::asiolink; +using namespace isc::util; + +namespace isc { +namespace dhcp { + +const size_t CfgMgr::CONFIG_LIST_SIZE = 10; + +CfgMgr& +CfgMgr::instance() { + static CfgMgr cfg_mgr; + return (cfg_mgr); +} + +Optional<std::string> +CfgMgr::getDataDir() const { + return (datadir_); +} + +void +CfgMgr::setDataDir(const std::string& datadir, bool unspecified) { + datadir_ = Optional<std::string>(datadir, unspecified); +} + +void +CfgMgr::setD2ClientConfig(D2ClientConfigPtr& new_config) { + ensureCurrentAllocated(); + // Note that D2ClientMgr::setD2Config() actually attempts to apply the + // configuration by stopping its sender and opening a new one and so + // forth per the new configuration. + d2_client_mgr_.setD2ClientConfig(new_config); + + // Manager will throw if the set fails, if it succeeds + // we'll update our SrvConfig, configuration_, with the D2ClientConfig + // used. This is largely bookkeeping in case we ever want to compare + // configuration_ to another SrvConfig. + configuration_->setD2ClientConfig(new_config); +} + +bool +CfgMgr::ddnsEnabled() { + return (d2_client_mgr_.ddnsEnabled()); +} + +const D2ClientConfigPtr& +CfgMgr::getD2ClientConfig() const { + return (d2_client_mgr_.getD2ClientConfig()); +} + +D2ClientMgr& +CfgMgr::getD2ClientMgr() { + return (d2_client_mgr_); +} + +void +CfgMgr::ensureCurrentAllocated() { + if (!configuration_ || configs_.empty()) { + configuration_.reset(new SrvConfig()); + configs_.push_back(configuration_); + } +} + +void +CfgMgr::clear() { + if (configuration_) { + configuration_->removeStatistics(); + } + configs_.clear(); + external_configs_.clear(); + D2ClientConfigPtr d2_default_conf(new D2ClientConfig()); + setD2ClientConfig(d2_default_conf); +} + +void +CfgMgr::commit() { + ensureCurrentAllocated(); + + // First we need to remove statistics. The new configuration can have fewer + // subnets. Also, it may change subnet-ids. So we need to remove them all + // and add it back. + configuration_->removeStatistics(); + + 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); + } + } + + // Set the last commit timestamp. + auto now = boost::posix_time::second_clock::universal_time(); + configuration_->setLastCommitTime(now); + + // Now we need to set the statistics back. + configuration_->updateStatistics(); + + configuration_->configureLowerLevelLibraries(); +} + +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(); +} + +SrvConfigPtr +CfgMgr::getCurrentCfg() { + ensureCurrentAllocated(); + 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()); +} + +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) { + try { + // First we need to remove statistics. + getCurrentCfg()->removeStatistics(); + mergeIntoCfg(getCurrentCfg(), seq); + + } catch (...) { + // Make sure the statistics is updated even if the merge failed. + getCurrentCfg()->updateStatistics(); + throw; + } + getCurrentCfg()->updateStatistics(); +} + +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, true), d2_client_mgr_(), family_(AF_INET) { + // 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 + // See AM_CPPFLAGS definition in Makefile.am +} + +CfgMgr::~CfgMgr() { +} + +} // end of isc::dhcp namespace +} // end of isc namespace |