Commit d7267561 authored by Thomas Markwalder's avatar Thomas Markwalder

[master] Merge branch 'trac3059'

Adds initial implementation of D2UpdateMgr class to
src/bin/d2.
parents feeb79f5 0fc5b23e
...@@ -56,6 +56,7 @@ b10_dhcp_ddns_SOURCES += d2_config.cc d2_config.h ...@@ -56,6 +56,7 @@ b10_dhcp_ddns_SOURCES += d2_config.cc d2_config.h
b10_dhcp_ddns_SOURCES += d2_cfg_mgr.cc d2_cfg_mgr.h b10_dhcp_ddns_SOURCES += d2_cfg_mgr.cc d2_cfg_mgr.h
b10_dhcp_ddns_SOURCES += d2_queue_mgr.cc d2_queue_mgr.h b10_dhcp_ddns_SOURCES += d2_queue_mgr.cc d2_queue_mgr.h
b10_dhcp_ddns_SOURCES += d2_update_message.cc d2_update_message.h b10_dhcp_ddns_SOURCES += d2_update_message.cc d2_update_message.h
b10_dhcp_ddns_SOURCES += d2_update_mgr.cc d2_update_mgr.h
b10_dhcp_ddns_SOURCES += d2_zone.cc d2_zone.h b10_dhcp_ddns_SOURCES += d2_zone.cc d2_zone.h
b10_dhcp_ddns_SOURCES += dns_client.cc dns_client.h b10_dhcp_ddns_SOURCES += dns_client.cc dns_client.h
...@@ -69,7 +70,7 @@ b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/asiodns/libb10-asiodns.la ...@@ -69,7 +70,7 @@ b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/asiodns/libb10-asiodns.la
b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libb10-dhcp_ddns.la b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libb10-dhcp_ddns.la
b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/util/libb10-util.la b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <d2/d2_log.h> #include <d2/d2_log.h>
#include <d2/d2_cfg_mgr.h> #include <d2/d2_cfg_mgr.h>
#include <util/encode/hex.h>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
...@@ -39,7 +40,7 @@ D2CfgContext::D2CfgContext(const D2CfgContext& rhs) : DCfgContextBase(rhs) { ...@@ -39,7 +40,7 @@ D2CfgContext::D2CfgContext(const D2CfgContext& rhs) : DCfgContextBase(rhs) {
reverse_mgr_->setDomains(rhs.reverse_mgr_->getDomains()); reverse_mgr_->setDomains(rhs.reverse_mgr_->getDomains());
} }
keys_ = rhs.keys_; keys_ = rhs.keys_;
} }
D2CfgContext::~D2CfgContext() { D2CfgContext::~D2CfgContext() {
...@@ -47,6 +48,10 @@ D2CfgContext::~D2CfgContext() { ...@@ -47,6 +48,10 @@ D2CfgContext::~D2CfgContext() {
// *********************** D2CfgMgr ************************* // *********************** D2CfgMgr *************************
const char* D2CfgMgr::IPV4_REV_ZONE_SUFFIX = "in-addr.arpa.";
const char* D2CfgMgr::IPV6_REV_ZONE_SUFFIX = "ip6.arpa.";
D2CfgMgr::D2CfgMgr() : DCfgMgrBase(DCfgContextBasePtr(new D2CfgContext())) { D2CfgMgr::D2CfgMgr() : DCfgMgrBase(DCfgContextBasePtr(new D2CfgContext())) {
// TSIG keys need to parse before the Domains, so we can catch Domains // TSIG keys need to parse before the Domains, so we can catch Domains
// that specify undefined keys. Create the necessary parsing order now. // that specify undefined keys. Create the necessary parsing order now.
...@@ -76,17 +81,79 @@ D2CfgMgr::matchForward(const std::string& fqdn, DdnsDomainPtr& domain) { ...@@ -76,17 +81,79 @@ D2CfgMgr::matchForward(const std::string& fqdn, DdnsDomainPtr& domain) {
} }
bool bool
D2CfgMgr::matchReverse(const std::string& fqdn, DdnsDomainPtr& domain) { D2CfgMgr::matchReverse(const std::string& ip_address, DdnsDomainPtr& domain) {
if (fqdn.empty()) { // Note, reverseIpAddress will throw if the ip_address is invalid.
// This is a programmatic error and should not happen. std::string reverse_address = reverseIpAddress(ip_address);
isc_throw(D2CfgError, "matchReverse passed a null or empty fqdn");
}
// Fetch the reverse manager from the D2 context. // Fetch the reverse manager from the D2 context.
DdnsDomainListMgrPtr mgr = getD2CfgContext()->getReverseMgr(); DdnsDomainListMgrPtr mgr = getD2CfgContext()->getReverseMgr();
// Call the manager's match method and return the result. return (mgr->matchDomain(reverse_address, domain));
return (mgr->matchDomain(fqdn, domain)); }
std::string
D2CfgMgr::reverseIpAddress(const std::string& address) {
try {
// Convert string address into an IOAddress and invoke the
// appropriate reverse method.
isc::asiolink::IOAddress ioaddr(address);
if (ioaddr.isV4()) {
return (reverseV4Address(ioaddr));
}
return (reverseV6Address(ioaddr));
} catch (const isc::Exception& ex) {
isc_throw(D2CfgError, "D2CfgMgr cannot reverse address: "
<< address << " : " << ex.what());
}
}
std::string
D2CfgMgr::reverseV4Address(const isc::asiolink::IOAddress& ioaddr) {
if (!ioaddr.isV4()) {
isc_throw(D2CfgError, "D2CfgMgr address is not IPv4 address :"
<< ioaddr.toText());
}
// Get the address in byte vector form.
std::vector<uint8_t> bytes = ioaddr.toBytes();
// Walk backwards through vector outputting each octet and a dot.
std::ostringstream stream;
std::vector<uint8_t>::const_reverse_iterator rit;
for (rit = bytes.rbegin(); rit != bytes.rend(); ++rit) {
stream << static_cast<unsigned int>(*rit) << ".";
}
// Tack on the suffix and we're done.
stream << IPV4_REV_ZONE_SUFFIX;
return(stream.str());
}
std::string
D2CfgMgr::reverseV6Address(const isc::asiolink::IOAddress& ioaddr) {
if (!ioaddr.isV6()) {
isc_throw(D2CfgError, "D2Cfg address is not IPv6 address: "
<< ioaddr.toText());
}
// Turn the address into a string of digits.
std::vector<uint8_t> bytes = ioaddr.toBytes();
std::string digits;
digits = isc::util::encode::encodeHex(bytes);
// Walk backwards through string outputting each digits and a dot.
std::ostringstream stream;
std::string::const_reverse_iterator rit;
for (rit = digits.rbegin(); rit != digits.rend(); ++rit) {
stream << static_cast<char>(*rit) << ".";
}
// Tack on the suffix and we're done.
stream << IPV6_REV_ZONE_SUFFIX;
return(stream.str());
} }
...@@ -99,10 +166,10 @@ D2CfgMgr::createConfigParser(const std::string& config_id) { ...@@ -99,10 +166,10 @@ D2CfgMgr::createConfigParser(const std::string& config_id) {
isc::dhcp::DhcpConfigParser* parser = NULL; isc::dhcp::DhcpConfigParser* parser = NULL;
if ((config_id == "interface") || if ((config_id == "interface") ||
(config_id == "ip_address")) { (config_id == "ip_address")) {
parser = new isc::dhcp::StringParser(config_id, parser = new isc::dhcp::StringParser(config_id,
context->getStringStorage()); context->getStringStorage());
} else if (config_id == "port") { } else if (config_id == "port") {
parser = new isc::dhcp::Uint32Parser(config_id, parser = new isc::dhcp::Uint32Parser(config_id,
context->getUint32Storage()); context->getUint32Storage());
} else if (config_id == "forward_ddns") { } else if (config_id == "forward_ddns") {
parser = new DdnsDomainListMgrParser("forward_mgr", parser = new DdnsDomainListMgrParser("forward_mgr",
......
...@@ -105,6 +105,14 @@ typedef boost::shared_ptr<DdnsDomainListMgr> DdnsDomainListMgrPtr; ...@@ -105,6 +105,14 @@ typedef boost::shared_ptr<DdnsDomainListMgr> DdnsDomainListMgrPtr;
/// and retrieving the information on demand. /// and retrieving the information on demand.
class D2CfgMgr : public DCfgMgrBase { class D2CfgMgr : public DCfgMgrBase {
public: public:
/// @brief Reverse zone suffix added to IPv4 addresses for reverse lookups
/// @todo This should be configurable.
static const char* IPV4_REV_ZONE_SUFFIX;
/// @brief Reverse zone suffix added to IPv6 addresses for reverse lookups
/// @todo This should be configurable.
static const char* IPV6_REV_ZONE_SUFFIX;
/// @brief Constructor /// @brief Constructor
D2CfgMgr(); D2CfgMgr();
...@@ -119,30 +127,84 @@ public: ...@@ -119,30 +127,84 @@ public:
} }
/// @brief Matches a given FQDN to a forward domain. /// @brief Matches a given FQDN to a forward domain.
/// ///
/// This calls the matchDomain method of the forward domain manager to /// This calls the matchDomain method of the forward domain manager to
/// match the given FQDN to a forward domain. /// match the given FQDN to a forward domain.
/// ///
/// @param fqdn is the name for which to look. /// @param fqdn is the name for which to look.
/// @param domain receives the matching domain. Note that it will be reset /// @param domain receives the matching domain. Note that it will be reset
/// upon entry and only set if a match is subsequently found. /// upon entry and only set if a match is subsequently found.
/// ///
/// @return returns true if a match is found, false otherwise. /// @return returns true if a match is found, false otherwise.
/// @throw throws D2CfgError if given an invalid fqdn. /// @throw throws D2CfgError if given an invalid fqdn.
bool matchForward(const std::string& fqdn, DdnsDomainPtr &domain); bool matchForward(const std::string& fqdn, DdnsDomainPtr& domain);
/// @brief Matches a given FQDN to a reverse domain. /// @brief Matches a given IP address to a reverse domain.
/// ///
/// This calls the matchDomain method of the reverse domain manager to /// This calls the matchDomain method of the reverse domain manager to
/// match the given FQDN to a forward domain. /// match the given IPv4 or IPv6 address to a reverse domain.
/// ///
/// @param fqdn is the name for which to look. /// @param ip_address is the name for which to look.
/// @param domain receives the matching domain. Note that it will be reset /// @param domain receives the matching domain. Note that it will be reset
/// upon entry and only set if a match is subsequently found. /// upon entry and only set if a match is subsequently found.
/// ///
/// @return returns true if a match is found, false otherwise. /// @return returns true if a match is found, false otherwise.
/// @throw throws D2CfgError if given an invalid fqdn. /// @throw throws D2CfgError if given an invalid fqdn.
bool matchReverse(const std::string& fqdn, DdnsDomainPtr &domain); bool matchReverse(const std::string& ip_address, DdnsDomainPtr& domain);
/// @brief Generate a reverse order string for the given IP address
///
/// This method creates a string containing the given IP address
/// contents in reverse order. This format is used for matching
/// against reverse DDNS domains in DHCP_DDNS configuration.
/// After reversing the syllables of the address, it appends the
/// appropriate suffix.
///
/// @param address string containing a valid IPv4 or IPv6 address.
///
/// @return a std::string containing the reverse order address.
///
/// @throw D2CfgError if given an invalid address.
static std::string reverseIpAddress(const std::string& address);
/// @brief Generate a reverse order string for the given IP address
///
/// This method creates a string containing the given IP address
/// contents in reverse order. This format is used for matching
/// against reverse DDNS domains in DHCP_DDNS configuration.
/// After reversing the syllables of the address, it appends the
/// appropriate suffix.
///
/// Example:
/// input: 192.168.1.15
/// output: 15.1.168.192.in-addr.arpa.
///
/// @param ioaddr is the IPv4 IOaddress to convert
///
/// @return a std::string containing the reverse order address.
///
/// @throw D2CfgError if not given an IPv4 address.
static std::string reverseV4Address(const isc::asiolink::IOAddress& ioaddr);
/// @brief Generate a reverse order string for the given IP address
///
/// This method creates a string containing the given IPv6 address
/// contents in reverse order. This format is used for matching
/// against reverse DDNS domains in DHCP_DDNS configuration.
/// After reversing the syllables of the address, it appends the
/// appropriate suffix.
///
/// IPv6 example:
/// input: 2001:db8:302:99::
/// output:
///0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.9.0.0.2.0.3.0.8.B.D.0.1.0.0.2.ip6.arpa.
///
/// @param address string containing a valid IPv6 address.
///
/// @return a std::string containing the reverse order address.
///
/// @throw D2CfgError if not given an IPv6 address.
static std::string reverseV6Address(const isc::asiolink::IOAddress& ioaddr);
protected: protected:
/// @brief Given an element_id returns an instance of the appropriate /// @brief Given an element_id returns an instance of the appropriate
......
...@@ -91,44 +91,67 @@ DdnsDomainListMgr::setDomains(DdnsDomainMapPtr domains) { ...@@ -91,44 +91,67 @@ DdnsDomainListMgr::setDomains(DdnsDomainMapPtr domains) {
bool bool
DdnsDomainListMgr::matchDomain(const std::string& fqdn, DdnsDomainPtr& domain) { DdnsDomainListMgr::matchDomain(const std::string& fqdn, DdnsDomainPtr& domain) {
// Clear the return parameter.
domain.reset();
// First check the case of one domain to rule them all. // First check the case of one domain to rule them all.
if ((size() == 1) && (wildcard_domain_)) { if ((size() == 1) && (wildcard_domain_)) {
domain = wildcard_domain_; domain = wildcard_domain_;
return (true); return (true);
} }
// Start with the longest version of the fqdn and search the list. // Iterate over the domain map looking for the domain which matches
// Continue looking for shorter versions of fqdn so long as no match is // the longest portion of the given fqdn.
// found.
// @todo This can surely be optimized, time permitting. size_t req_len = fqdn.size();
std::string match_name = fqdn; size_t match_len = 0;
std::size_t start_pos = 0; DdnsDomainMapPair map_pair;
while (start_pos != std::string::npos) { DdnsDomainPtr best_match;
match_name = match_name.substr(start_pos, std::string::npos); BOOST_FOREACH (map_pair, *domains_) {
DdnsDomainMap::iterator gotit = domains_->find(match_name); std::string domain_name = map_pair.first;
if (gotit != domains_->end()) { size_t dom_len = domain_name.size();
domain = gotit->second;
return (true); // If the domain name is longer than the fqdn, then it cant be match.
if (req_len < dom_len) {
continue;
} }
start_pos = match_name.find_first_of("."); // If the lengths are identical and the names match we're done.
if (start_pos != std::string::npos) { if (req_len == dom_len) {
++start_pos; if (fqdn == domain_name) {
// exact match, done
domain = map_pair.second;
return (true);
}
} else {
// The fqdn is longer than the domain name. Adjust the start
// point of comparison by the excess in length. Only do the
// comparison if the adjustment lands on a boundary. This
// prevents "onetwo.net" from matching "two.net".
size_t offset = req_len - dom_len;
if ((fqdn[offset - 1] == '.') &&
(fqdn.compare(offset, std::string::npos, domain_name) == 0)) {
// Fqdn contains domain name, keep it if its better than
// any we have matched so far.
if (dom_len > match_len) {
match_len = dom_len;
best_match = map_pair.second;
}
}
} }
} }
// There's no match. If they specified a wild card domain use it if (!best_match) {
// otherwise there's no domain for this entry. // There's no match. If they specified a wild card domain use it
if (wildcard_domain_) { // otherwise there's no domain for this entry.
domain = wildcard_domain_; if (wildcard_domain_) {
return (true); domain = wildcard_domain_;
return (true);
}
LOG_WARN(dctl_logger, DHCP_DDNS_NO_MATCH).arg(fqdn);
return (false);
} }
LOG_WARN(dctl_logger, DHCP_DDNS_NO_MATCH).arg(fqdn); domain = best_match;
return (false); return (true);
} }
// *************************** PARSERS *********************************** // *************************** PARSERS ***********************************
......
...@@ -321,8 +321,8 @@ typedef boost::shared_ptr<DnsServerInfoStorage> DnsServerInfoStoragePtr; ...@@ -321,8 +321,8 @@ typedef boost::shared_ptr<DnsServerInfoStorage> DnsServerInfoStoragePtr;
/// it. It's primary use is to map a domain to the DNS server(s) responsible /// it. It's primary use is to map a domain to the DNS server(s) responsible
/// for it. /// for it.
/// @todo Currently the name entry for a domain is just an std::string. It /// @todo Currently the name entry for a domain is just an std::string. It
/// may be worthwhile to change this to a dns::Name for purposes of better /// may be worthwhile to change this to a dns::Name for purposes of better
/// validation and matching capabilities. /// validation and matching capabilities.
class DdnsDomain { class DdnsDomain {
public: public:
/// @brief Constructor /// @brief Constructor
...@@ -385,7 +385,11 @@ typedef boost::shared_ptr<DdnsDomainMap> DdnsDomainMapPtr; ...@@ -385,7 +385,11 @@ typedef boost::shared_ptr<DdnsDomainMap> DdnsDomainMapPtr;
/// services. These services are used to match a FQDN to a domain. Currently /// services. These services are used to match a FQDN to a domain. Currently
/// it supports a single matching service, which will return the matching /// it supports a single matching service, which will return the matching
/// domain or a wild card domain if one is specified. The wild card domain is /// domain or a wild card domain if one is specified. The wild card domain is
/// specified as a domain whose name is "*". /// specified as a domain whose name is "*". The wild card domain will match
/// any entry and is provided for flexibility in FQDNs If for instance, all
/// forward requests are handled by the same servers, the configuration could
/// specify the wild card domain as the only forward domain. All forward DNS
/// updates would be sent to that one list of servers, regardless of the FQDN.
/// As matching capabilities evolve this class is expected to expand. /// As matching capabilities evolve this class is expected to expand.
class DdnsDomainListMgr { class DdnsDomainListMgr {
public: public:
...@@ -410,8 +414,8 @@ public: ...@@ -410,8 +414,8 @@ public:
/// it will be returned immediately for any FQDN. /// it will be returned immediately for any FQDN.
/// ///
/// @param fqdn is the name for which to look. /// @param fqdn is the name for which to look.
/// @param domain receives the matching domain. Note that it will be reset /// @param domain receives the matching domain. If no match is found its
/// upon entry and only set if a match is subsequently found. /// contents will be unchanged.
/// ///
/// @return returns true if a match is found, false otherwise. /// @return returns true if a match is found, false otherwise.
/// @todo This is a very basic match method, which expects valid FQDNs /// @todo This is a very basic match method, which expects valid FQDNs
......
...@@ -112,6 +112,10 @@ service first starts. ...@@ -112,6 +112,10 @@ service first starts.
This is an informational message issued when the controller is exiting This is an informational message issued when the controller is exiting
following a shut down (normal or otherwise) of the service. following a shut down (normal or otherwise) of the service.
% DHCP_DDNS_AT_MAX_TRANSACTIONS application has %1 queued requests but has reached maximum number of %2 concurrent transactions
This is a debug message that indicates that the application has DHCP_DDNS
requests in the queue but is working as many concurrent requests as allowed.
% DHCP_DDNS_COMMAND command directive received, command: %1 - args: %2 % DHCP_DDNS_COMMAND command directive received, command: %1 - args: %2
This is a debug message issued when the Dhcp-Ddns application command method This is a debug message issued when the Dhcp-Ddns application command method
has been invoked. has been invoked.
...@@ -129,16 +133,35 @@ This is a debug message issued when the DHCP-DDNS application encountered an ...@@ -129,16 +133,35 @@ This is a debug message issued when the DHCP-DDNS application encountered an
error while decoding a response to DNS Update message. Typically, this error error while decoding a response to DNS Update message. Typically, this error
will be encountered when a response message is malformed. will be encountered when a response message is malformed.
% DHCP_DDNS_NO_MATCH No DNS servers match FQDN: %1 % DHCP_DDNS_NO_ELIGIBLE_JOBS although there are queued requests, there are pending transactions for each Queue count: %1 Transaction count: %2
This is a debug messge issued when all of the queued requests represent clients
for which there is a an update already in progress. This may occur under
normal operations but should be temporary situation.
% DHCP_DDNS_NO_FWD_MATCH_ERROR the configured list of forward DDNS domains does not contain a match for FQDN %1 The request has been discarded.
This is an error message that indicates that DHCP_DDNS received a request to
update a the forward DNS information for the given FQDN but for which there are
no configured DDNS domains in the DHCP_DDNS configuration. Either the DHCP_DDNS
configuration needs to be updated or the source of the FQDN itself should be
investigated.
% DHCP_DDNS_NO_MATCH No DNS servers match FQDN %1
This is warning message issued when there are no domains in the configuration This is warning message issued when there are no domains in the configuration
which match the cited fully qualified domain name (FQDN). The DNS Update which match the cited fully qualified domain name (FQDN). The DNS Update
request for the FQDN cannot be processed. request for the FQDN cannot be processed.
% DHCP_DDNS_NO_REV_MATCH_ERROR the configured list of reverse DDNS domains does not contain a match for FQDN %1 The request has been discarded.
This is an error message that indicates that DHCP_DDNS received a request to
update a the reverse DNS information for the given FQDN but for which there are
no configured DDNS domains in the DHCP_DDNS configuration. Either the DHCP_DDNS
configuration needs to be updated or the source of the FQDN itself should be
investigated.
% DHCP_DDNS_PROCESS_INIT application init invoked % DHCP_DDNS_PROCESS_INIT application init invoked
This is a debug message issued when the Dhcp-Ddns application enters This is a debug message issued when the Dhcp-Ddns application enters
its init method. its init method.
% DHCP_DDNS_QUEUE_MGR_QUEUE_FULL application request queue has reached maximum number of entries: %1 % DHCP_DDNS_QUEUE_MGR_QUEUE_FULL application request queue has reached maximum number of entries %1
This an error message indicating that DHCP-DDNS is receiving DNS update This an error message indicating that DHCP-DDNS is receiving DNS update
requests faster than they can be processed. This may mean the maximum queue requests faster than they can be processed. This may mean the maximum queue
needs to be increased, the DHCP-DDNS clients are simply generating too many needs to be increased, the DHCP-DDNS clients are simply generating too many
......
// Copyright (C) 2013 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 <d2/d2_update_mgr.h>
#include <sstream>
#include <iostream>
#include <vector>
namespace isc {
namespace d2 {
const size_t D2UpdateMgr::MAX_TRANSACTIONS_DEFAULT;
D2UpdateMgr::D2UpdateMgr(D2QueueMgrPtr& queue_mgr, D2CfgMgrPtr& cfg_mgr,
isc::asiolink::IOService& io_service,
const size_t max_transactions)
:queue_mgr_(queue_mgr), cfg_mgr_(cfg_mgr), io_service_(io_service) {
if (!queue_mgr_) {
isc_throw(D2UpdateMgrError, "D2UpdateMgr queue manager cannot be null");
}
if (!cfg_mgr_) {
isc_throw(D2UpdateMgrError,
"D2UpdateMgr configuration manager cannot be null");
}
// Use setter to do validation.
setMaxTransactions(max_transactions);
}
D2UpdateMgr::~D2UpdateMgr() {
transaction_list_.clear();
}
void D2UpdateMgr::sweep() {
// cleanup finished transactions;
checkFinishedTransactions();
// if the queue isn't empty, find the next suitable job and
// start a transaction for it.
// @todo - Do we want to queue max transactions? The logic here will only
// start one new transaction per invocation. On the other hand a busy
// system will generate many IO events and this method will be called
// frequently. It will likely achieve max transactions quickly on its own.
if (getQueueCount() > 0) {
if (getTransactionCount() >= max_transactions_) {
LOG_DEBUG(dctl_logger, DBGLVL_TRACE_DETAIL_DATA,
DHCP_DDNS_AT_MAX_TRANSACTIONS).arg(getQueueCount())
.arg(getMaxTransactions());
return;
}
// We are not at maximum transactions, so pick and start the next job.
pickNextJob();
}
}
void
D2UpdateMgr::checkFinishedTransactions() {
// Cycle through transaction list and do whatever needs to be done
// for finished transactions.
// At the moment all we do is remove them from the list. This is likely
// to expand as DHCP_DDNS matures.
TransactionList::iterator it = transaction_list_.begin();
while (it != transaction_list_.end()) {
NameChangeTransactionPtr trans = (*it).second;
switch (trans->getNcrStatus()) {
case dhcp_ddns::ST_COMPLETED:
transaction_list_.erase(it);
break;
case dhcp_ddns::ST_FAILED:
transaction_list_.erase(it);
break;
default:
break;
}
++it;
}
}
void D2UpdateMgr::pickNextJob() {
// Start at the front of the queue, looking for the first entry for
// which no transaction is in progress. If we find an eligible entry
// remove it from the queue and make a transaction for it.
// Requests and transactions are associated by DHCID. If a request has
// the same DHCID as a transaction, they are presumed to be for the same
// "end user".
size_t queue_count = getQueueCount();
for (size_t index = 0; index < queue_count; ++index) {
dhcp_ddns::NameChangeRequestPtr found_ncr = queue_mgr_->peekAt(index);
if (!hasTransaction(found_ncr->getDhcid())) {
queue_mgr_->dequeueAt(index);
makeTransaction(found_ncr);
return;
}
}
// There were no eligible jobs. All of the current DHCIDs already have
// transactions pending.
LOG_DEBUG(dctl_logger, DBGLVL_TRACE_DETAIL_DATA, DHCP_DDNS_NO_ELIGIBLE_JOBS)
.arg(getQueueCount()).arg(getTransactionCount());
}
void
D2UpdateMgr::makeTransaction(dhcp_ddns::NameChangeRequestPtr& next_ncr) {
// First lets ensure there is not a transaction in progress for this
// DHCID. (pickNextJob should ensure this, as it is the only real caller
// but for safety's sake we'll check).
const TransactionKey& key = next_ncr->getDhcid();
if (findTransaction(key) != transactionListEnd()) {
// This is programmatic error. Caller(s) should be checking this.
isc_throw(D2UpdateMgrError, "Transaction already in progress for: "
<< key.toStr());