Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Kea
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
445
Issues
445
List
Boards
Labels
Service Desk
Milestones
Merge Requests
71
Merge Requests
71
Operations
Operations
Incidents
Packages & Registries
Packages & Registries
Container Registry
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
ISC Open Source Projects
Kea
Commits
d7267561
Commit
d7267561
authored
Aug 07, 2013
by
Thomas Markwalder
Browse files
Options
Browse Files
Download
Plain Diff
[master] Merge branch 'trac3059'
Adds initial implementation of D2UpdateMgr class to src/bin/d2.
parents
feeb79f5
0fc5b23e
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
1254 additions
and
71 deletions
+1254
-71
src/bin/d2/Makefile.am
src/bin/d2/Makefile.am
+2
-1
src/bin/d2/d2_cfg_mgr.cc
src/bin/d2/d2_cfg_mgr.cc
+77
-10
src/bin/d2/d2_cfg_mgr.h
src/bin/d2/d2_cfg_mgr.h
+71
-9
src/bin/d2/d2_config.cc
src/bin/d2/d2_config.cc
+48
-25
src/bin/d2/d2_config.h
src/bin/d2/d2_config.h
+9
-5
src/bin/d2/d2_messages.mes
src/bin/d2/d2_messages.mes
+25
-2
src/bin/d2/d2_update_mgr.cc
src/bin/d2/d2_update_mgr.cc
+227
-0
src/bin/d2/d2_update_mgr.h
src/bin/d2/d2_update_mgr.h
+294
-0
src/bin/d2/tests/Makefile.am
src/bin/d2/tests/Makefile.am
+2
-0
src/bin/d2/tests/d2_cfg_mgr_unittests.cc
src/bin/d2/tests/d2_cfg_mgr_unittests.cc
+35
-18
src/bin/d2/tests/d2_update_mgr_unittests.cc
src/bin/d2/tests/d2_update_mgr_unittests.cc
+443
-0
src/lib/dhcp_ddns/ncr_msg.h
src/lib/dhcp_ddns/ncr_msg.h
+6
-1
src/lib/dhcp_ddns/tests/ncr_unittests.cc
src/lib/dhcp_ddns/tests/ncr_unittests.cc
+15
-0
No files found.
src/bin/d2/Makefile.am
View file @
d7267561
...
...
@@ -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_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_mgr.cc d2_update_mgr.h
b10_dhcp_ddns_SOURCES
+=
d2_zone.cc d2_zone.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
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/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/util/libb10-util.la
b10_dhcp_ddns_LDADD
+=
$(top_builddir)
/src/lib/hooks/libb10-hooks.la
...
...
src/bin/d2/d2_cfg_mgr.cc
View file @
d7267561
...
...
@@ -14,6 +14,7 @@
#include <d2/d2_log.h>
#include <d2/d2_cfg_mgr.h>
#include <util/encode/hex.h>
#include <boost/foreach.hpp>
...
...
@@ -39,7 +40,7 @@ D2CfgContext::D2CfgContext(const D2CfgContext& rhs) : DCfgContextBase(rhs) {
reverse_mgr_
->
setDomains
(
rhs
.
reverse_mgr_
->
getDomains
());
}
keys_
=
rhs
.
keys_
;
keys_
=
rhs
.
keys_
;
}
D2CfgContext
::~
D2CfgContext
()
{
...
...
@@ -47,6 +48,10 @@ D2CfgContext::~D2CfgContext() {
// *********************** 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
()))
{
// TSIG keys need to parse before the Domains, so we can catch Domains
// that specify undefined keys. Create the necessary parsing order now.
...
...
@@ -76,17 +81,79 @@ D2CfgMgr::matchForward(const std::string& fqdn, DdnsDomainPtr& domain) {
}
bool
D2CfgMgr
::
matchReverse
(
const
std
::
string
&
fqdn
,
DdnsDomainPtr
&
domain
)
{
if
(
fqdn
.
empty
())
{
// This is a programmatic error and should not happen.
isc_throw
(
D2CfgError
,
"matchReverse passed a null or empty fqdn"
);
}
D2CfgMgr
::
matchReverse
(
const
std
::
string
&
ip_address
,
DdnsDomainPtr
&
domain
)
{
// Note, reverseIpAddress will throw if the ip_address is invalid.
std
::
string
reverse_address
=
reverseIpAddress
(
ip_address
);
// Fetch the reverse manager from the D2 context.
DdnsDomainListMgrPtr
mgr
=
getD2CfgContext
()
->
getReverseMgr
();
// Call the manager's match method and return the result.
return
(
mgr
->
matchDomain
(
fqdn
,
domain
));
return
(
mgr
->
matchDomain
(
reverse_address
,
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) {
isc
::
dhcp
::
DhcpConfigParser
*
parser
=
NULL
;
if
((
config_id
==
"interface"
)
||
(
config_id
==
"ip_address"
))
{
parser
=
new
isc
::
dhcp
::
StringParser
(
config_id
,
parser
=
new
isc
::
dhcp
::
StringParser
(
config_id
,
context
->
getStringStorage
());
}
else
if
(
config_id
==
"port"
)
{
parser
=
new
isc
::
dhcp
::
Uint32Parser
(
config_id
,
parser
=
new
isc
::
dhcp
::
Uint32Parser
(
config_id
,
context
->
getUint32Storage
());
}
else
if
(
config_id
==
"forward_ddns"
)
{
parser
=
new
DdnsDomainListMgrParser
(
"forward_mgr"
,
...
...
src/bin/d2/d2_cfg_mgr.h
View file @
d7267561
...
...
@@ -105,6 +105,14 @@ typedef boost::shared_ptr<DdnsDomainListMgr> DdnsDomainListMgrPtr;
/// and retrieving the information on demand.
class
D2CfgMgr
:
public
DCfgMgrBase
{
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
D2CfgMgr
();
...
...
@@ -119,30 +127,84 @@ public:
}
/// @brief Matches a given FQDN to a forward domain.
///
///
/// 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 domain receives the matching domain. Note that it will be reset
/// upon entry and only set if a match is subsequently found.
///
/// @return returns true if a match is found, false otherwise.
/// @throw throws D2CfgError if given an invalid fqdn.
bool
matchForward
(
const
std
::
string
&
fqdn
,
DdnsDomainPtr
&
domain
);
/// @throw throws D2CfgError if given an invalid fqdn.
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
/// 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
/// upon entry and only set if a match is subsequently found.
///
/// @return returns true if a match is found, false otherwise.
/// @throw throws D2CfgError if given an invalid fqdn.
bool
matchReverse
(
const
std
::
string
&
fqdn
,
DdnsDomainPtr
&
domain
);
/// @throw throws D2CfgError if given an invalid fqdn.
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:
/// @brief Given an element_id returns an instance of the appropriate
...
...
src/bin/d2/d2_config.cc
View file @
d7267561
...
...
@@ -91,44 +91,67 @@ DdnsDomainListMgr::setDomains(DdnsDomainMapPtr domains) {
bool
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.
if
((
size
()
==
1
)
&&
(
wildcard_domain_
))
{
domain
=
wildcard_domain_
;
return
(
true
);
}
// Start with the longest version of the fqdn and search the list.
// Continue looking for shorter versions of fqdn so long as no match is
// found.
// @todo This can surely be optimized, time permitting.
std
::
string
match_name
=
fqdn
;
std
::
size_t
start_pos
=
0
;
while
(
start_pos
!=
std
::
string
::
npos
)
{
match_name
=
match_name
.
substr
(
start_pos
,
std
::
string
::
npos
);
DdnsDomainMap
::
iterator
gotit
=
domains_
->
find
(
match_name
);
if
(
gotit
!=
domains_
->
end
())
{
domain
=
gotit
->
second
;
return
(
true
);
// Iterate over the domain map looking for the domain which matches
// the longest portion of the given fqdn.
size_t
req_len
=
fqdn
.
size
();
size_t
match_len
=
0
;
DdnsDomainMapPair
map_pair
;
DdnsDomainPtr
best_match
;
BOOST_FOREACH
(
map_pair
,
*
domains_
)
{
std
::
string
domain_name
=
map_pair
.
first
;
size_t
dom_len
=
domain_name
.
size
();
// 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
(
start_pos
!=
std
::
string
::
npos
)
{
++
start_pos
;
// If the lengths are identical and the names match we're done.
if
(
req_len
==
dom_len
)
{
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
// otherwise there's no domain for this entry.
if
(
wildcard_domain_
)
{
domain
=
wildcard_domain_
;
return
(
true
);
if
(
!
best_match
)
{
// There's no match. If they specified a wild card domain use it
// otherwise there's no domain for this entry.
if
(
wildcard_domain_
)
{
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
)
;
return
(
fals
e
);
domain
=
best_match
;
return
(
tru
e
);
}
// *************************** PARSERS ***********************************
...
...
src/bin/d2/d2_config.h
View file @
d7267561
...
...
@@ -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
/// for 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
/// validation and matching capabilities.
/// may be worthwhile to change this to a dns::Name for purposes of better
/// validation and matching capabilities.
class
DdnsDomain
{
public:
/// @brief Constructor
...
...
@@ -385,7 +385,11 @@ typedef boost::shared_ptr<DdnsDomainMap> DdnsDomainMapPtr;
/// services. These services are used to match a FQDN to a domain. Currently
/// 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
/// 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.
class
DdnsDomainListMgr
{
public:
...
...
@@ -410,8 +414,8 @@ public:
/// it will be returned immediately for any FQDN.
///
/// @param fqdn is the name for which to look.
/// @param domain receives the matching domain.
Note that it will be reset
///
upon entry and only set if a match is subsequently foun
d.
/// @param domain receives the matching domain.
If no match is found its
///
contents will be unchange
d.
///
/// @return returns true if a match is found, false otherwise.
/// @todo This is a very basic match method, which expects valid FQDNs
...
...
src/bin/d2/d2_messages.mes
View file @
d7267561
...
...
@@ -112,6 +112,10 @@ service first starts.
This is an informational message issued when the controller is exiting
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
This is a debug message issued when the Dhcp-Ddns application command method
has been invoked.
...
...
@@ -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
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
which match the cited fully qualified domain name (FQDN). The DNS Update
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
This is a debug message issued when the Dhcp-Ddns application enters
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
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
...
...
src/bin/d2/d2_update_mgr.cc
0 → 100644
View file @
d7267561
// 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
());
}
// If forward change is enabled, match to forward servers.
DdnsDomainPtr
forward_domain
;
if
(
next_ncr
->
isForwardChange
())
{
bool
matched
=
cfg_mgr_
->
matchForward
(
next_ncr
->
getFqdn
(),
forward_domain
);
// Could not find a match for forward DNS server. Log it and get out.
// This has the net affect of dropping the request on the floor.
if
(
!
matched
)
{
LOG_ERROR
(
dctl_logger
,
DHCP_DDNS_NO_FWD_MATCH_ERROR
)
.
arg
(
next_ncr
->
getFqdn
());
return
;
}
}
// If reverse change is enabled, match to reverse servers.
DdnsDomainPtr
reverse_domain
;
if
(
next_ncr
->
isReverseChange
())
{
bool
matched
=
cfg_mgr_
->
matchReverse
(
next_ncr
->
getIpAddress
(),
reverse_domain
);
// Could not find a match for reverse DNS server. Log it and get out.
// This has the net affect of dropping the request on the floor.
if
(
!
matched
)
{
LOG_ERROR
(
dctl_logger
,
DHCP_DDNS_NO_REV_MATCH_ERROR
)
.
arg
(
next_ncr
->
getIpAddress
());
return
;
}
}
// We matched to the required servers, so construct the transaction.
NameChangeTransactionPtr
trans
(
new
NameChangeTransaction
(
io_service_
,
next_ncr
,
forward_domain
,
reverse_domain
));
// Add the new transaction to the list.
transaction_list_
[
key
]
=
trans
;
}
TransactionList
::
iterator
D2UpdateMgr
::
findTransaction
(
const
TransactionKey
&
key
)
{
return
(
transaction_list_
.
find
(
key
));
}
bool
D2UpdateMgr
::
hasTransaction
(
const
TransactionKey
&
key
)
{
return
(
findTransaction
(
key
)
!=
transactionListEnd
());
}
void
D2UpdateMgr
::
removeTransaction
(
const
TransactionKey
&
key
)
{
TransactionList
::
iterator
pos
=
findTransaction
(
key
);
if
(
pos
!=
transactionListEnd
())
{
transaction_list_
.
erase
(
pos
);
}
}
TransactionList
::
iterator
D2UpdateMgr
::
transactionListEnd
()
{
return
(
transaction_list_
.
end
());
}
void
D2UpdateMgr
::
clearTransactionList
()
{
// @todo for now this just wipes them out. We might need something
// more elegant, that allows a cancel first.
transaction_list_
.
clear
();
}