Browse Source

[master] Merge branch 'trac3222'

b10-dhcp6 fully able to do DHCP-DDNS
Thomas Markwalder 11 years ago
parent
commit
2399566964

+ 4 - 3
src/bin/d2/d2_config.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-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
@@ -20,6 +20,7 @@
 
 
 #include <boost/foreach.hpp>
 #include <boost/foreach.hpp>
 #include <boost/lexical_cast.hpp>
 #include <boost/lexical_cast.hpp>
+#include <boost/algorithm/string/predicate.hpp>
 
 
 #include <string>
 #include <string>
 
 
@@ -131,7 +132,7 @@ DdnsDomainListMgr::matchDomain(const std::string& fqdn, DdnsDomainPtr& domain) {
 
 
         // If the lengths are identical and the names match we're done.
         // If the lengths are identical and the names match we're done.
         if (req_len == dom_len) {
         if (req_len == dom_len) {
-            if (fqdn == domain_name) {
+            if (boost::iequals(fqdn, domain_name)) {
                 // exact match, done
                 // exact match, done
                 domain = map_pair.second;
                 domain = map_pair.second;
                 return (true);
                 return (true);
@@ -143,7 +144,7 @@ DdnsDomainListMgr::matchDomain(const std::string& fqdn, DdnsDomainPtr& domain) {
             // prevents "onetwo.net" from matching "two.net".
             // prevents "onetwo.net" from matching "two.net".
             size_t offset = req_len - dom_len;
             size_t offset = req_len - dom_len;
             if ((fqdn[offset - 1] == '.')  &&
             if ((fqdn[offset - 1] == '.')  &&
-               (fqdn.compare(offset, std::string::npos, domain_name) == 0)) {
+               (boost::iequals(fqdn.substr(offset), domain_name))) {
                 // Fqdn contains domain name, keep it if its better than
                 // Fqdn contains domain name, keep it if its better than
                 // any we have matched so far.
                 // any we have matched so far.
                 if (dom_len > match_len) {
                 if (dom_len > match_len) {

+ 7 - 7
src/bin/d2/d2_messages.mes

@@ -344,10 +344,10 @@ due to invalid data contained in the NameChangeRequest. The request will be
 aborted.  This is most likely a configuration issue.
 aborted.  This is most likely a configuration issue.
 
 
 % DHCP_DDNS_ADD_SUCCEEDED DHCP_DDNS successfully added the DNS mapping addition for this request: %1
 % DHCP_DDNS_ADD_SUCCEEDED DHCP_DDNS successfully added the DNS mapping addition for this request: %1
-This is a debug message issued after DHCP_DDNS has submitted DNS mapping
-additions which were received and accepted by an appropriate DNS server.
+This is an informational message issued after DHCP_DDNS has submitted DNS
+mapping additions which were received and accepted by an appropriate DNS server.
 
 
-% DHCP_DDNS_ADD_FAILED DHCP_DDNS failed attempting to make DNS mapping additions for this request: %1, event: %2
+% DHCP_DDNS_ADD_FAILED DHCP_DDNS Transaction outcome: %1
 This is an error message issued after DHCP_DDNS attempts to submit DNS mapping
 This is an error message issued after DHCP_DDNS attempts to submit DNS mapping
 entry additions have failed.  The precise reason for the failure should be
 entry additions have failed.  The precise reason for the failure should be
 documented in preceding log entries.
 documented in preceding log entries.
@@ -430,17 +430,17 @@ while DHCP_DDNS was removing a reverse address mapping.  The request will be
 aborted.  This is most likely a programmatic issue and should be reported.
 aborted.  This is most likely a programmatic issue and should be reported.
 
 
 % DHCP_DDNS_REMOVE_SUCCEEDED DHCP_DDNS successfully removed the DNS mapping addition for this request: %1
 % DHCP_DDNS_REMOVE_SUCCEEDED DHCP_DDNS successfully removed the DNS mapping addition for this request: %1
-This is a debug message issued after DHCP_DDNS has submitted DNS mapping
-removals which were received and accepted by an appropriate DNS server.
+This is an informational message issued after DHCP_DDNS has submitted DNS
+mapping removals which were received and accepted by an appropriate DNS server.
 
 
-% DHCP_DDNS_REMOVE_FAILED DHCP_DDNS failed attempting to make DNS mapping removals for this request: %1, event: %2
+% DHCP_DDNS_REMOVE_FAILED DHCP_DDNS Transaction outcome: %1
 This is an error message issued after DHCP_DDNS attempts to submit DNS mapping
 This is an error message issued after DHCP_DDNS attempts to submit DNS mapping
 entry removals have failed.  The precise reason for the failure should be
 entry removals have failed.  The precise reason for the failure should be
 documented in preceding log entries.
 documented in preceding log entries.
 
 
 % DHCP_DDNS_STARTING_TRANSACTION Transaction Key: %1
 % DHCP_DDNS_STARTING_TRANSACTION Transaction Key: %1
 
 
-% DHCP_DDNS_UPDATE_REQUEST_SENT for transaction key: %1 to server: %2
+% DHCP_DDNS_UPDATE_REQUEST_SENT %1 for transaction key: %2 to server: %3
 This is a debug message issued when DHCP_DDNS sends a DNS request to a DNS
 This is a debug message issued when DHCP_DDNS sends a DNS request to a DNS
 server.
 server.
 
 

+ 7 - 7
src/bin/d2/nc_add.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-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
@@ -202,7 +202,7 @@ NameAddTransaction::addingFwdAddrsHandler() {
 
 
         // Call sendUpdate() to initiate the async send. Note it also sets
         // Call sendUpdate() to initiate the async send. Note it also sets
         // next event to NOP_EVT.
         // next event to NOP_EVT.
-        sendUpdate();
+        sendUpdate("Foward Add");
         break;
         break;
 
 
     case IO_COMPLETED_EVT: {
     case IO_COMPLETED_EVT: {
@@ -313,7 +313,7 @@ NameAddTransaction::replacingFwdAddrsHandler() {
 
 
         // Call sendUpdate() to initiate the async send. Note it also sets
         // Call sendUpdate() to initiate the async send. Note it also sets
         // next event to NOP_EVT.
         // next event to NOP_EVT.
-        sendUpdate();
+        sendUpdate("Forward Replace");
         break;
         break;
 
 
     case IO_COMPLETED_EVT: {
     case IO_COMPLETED_EVT: {
@@ -459,7 +459,7 @@ NameAddTransaction::replacingRevPtrsHandler() {
 
 
         // Call sendUpdate() to initiate the async send. Note it also sets
         // Call sendUpdate() to initiate the async send. Note it also sets
         // next event to NOP_EVT.
         // next event to NOP_EVT.
-        sendUpdate();
+        sendUpdate("Reverse Replace");
         break;
         break;
 
 
     case IO_COMPLETED_EVT: {
     case IO_COMPLETED_EVT: {
@@ -539,7 +539,7 @@ void
 NameAddTransaction::processAddOkHandler() {
 NameAddTransaction::processAddOkHandler() {
     switch(getNextEvent()) {
     switch(getNextEvent()) {
     case UPDATE_OK_EVT:
     case UPDATE_OK_EVT:
-        LOG_DEBUG(dctl_logger, DBGLVL_TRACE_DETAIL, DHCP_DDNS_ADD_SUCCEEDED)
+        LOG_INFO(dctl_logger, DHCP_DDNS_ADD_SUCCEEDED)
                   .arg(getNcr()->toText());
                   .arg(getNcr()->toText());
         setNcrStatus(dhcp_ddns::ST_COMPLETED);
         setNcrStatus(dhcp_ddns::ST_COMPLETED);
         endModel();
         endModel();
@@ -556,9 +556,9 @@ NameAddTransaction::processAddFailedHandler() {
     switch(getNextEvent()) {
     switch(getNextEvent()) {
     case UPDATE_FAILED_EVT:
     case UPDATE_FAILED_EVT:
     case NO_MORE_SERVERS_EVT:
     case NO_MORE_SERVERS_EVT:
-        LOG_ERROR(dctl_logger, DHCP_DDNS_ADD_FAILED).arg(getNcr()->toText())
-        .arg(getEventLabel(getNextEvent()));
         setNcrStatus(dhcp_ddns::ST_FAILED);
         setNcrStatus(dhcp_ddns::ST_FAILED);
+        LOG_ERROR(dctl_logger, DHCP_DDNS_ADD_FAILED)
+                  .arg(transactionOutcomeString());
         endModel();
         endModel();
         break;
         break;
     default:
     default:

+ 8 - 8
src/bin/d2/nc_remove.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-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
@@ -207,7 +207,7 @@ NameRemoveTransaction::removingFwdAddrsHandler() {
 
 
         // Call sendUpdate() to initiate the async send. Note it also sets
         // Call sendUpdate() to initiate the async send. Note it also sets
         // next event to NOP_EVT.
         // next event to NOP_EVT.
-        sendUpdate();
+        sendUpdate("Forward A/AAAA Remove");
         break;
         break;
 
 
     case IO_COMPLETED_EVT: {
     case IO_COMPLETED_EVT: {
@@ -311,7 +311,7 @@ NameRemoveTransaction::removingFwdRRsHandler() {
 
 
         // Call sendUpdate() to initiate the async send. Note it also sets
         // Call sendUpdate() to initiate the async send. Note it also sets
         // next event to NOP_EVT.
         // next event to NOP_EVT.
-        sendUpdate();
+        sendUpdate("Forward RR Remove");
         break;
         break;
 
 
     case IO_COMPLETED_EVT: {
     case IO_COMPLETED_EVT: {
@@ -464,7 +464,7 @@ NameRemoveTransaction::removingRevPtrsHandler() {
 
 
         // Call sendUpdate() to initiate the async send. Note it also sets
         // Call sendUpdate() to initiate the async send. Note it also sets
         // next event to NOP_EVT.
         // next event to NOP_EVT.
-        sendUpdate();
+        sendUpdate("Reverse Remove");
         break;
         break;
 
 
     case IO_COMPLETED_EVT: {
     case IO_COMPLETED_EVT: {
@@ -547,8 +547,8 @@ void
 NameRemoveTransaction::processRemoveOkHandler() {
 NameRemoveTransaction::processRemoveOkHandler() {
     switch(getNextEvent()) {
     switch(getNextEvent()) {
     case UPDATE_OK_EVT:
     case UPDATE_OK_EVT:
-        LOG_DEBUG(dctl_logger, DBGLVL_TRACE_DETAIL, DHCP_DDNS_REMOVE_SUCCEEDED)
-                  .arg(getNcr()->toText());
+        LOG_INFO(dctl_logger, DHCP_DDNS_REMOVE_SUCCEEDED)
+                .arg(getNcr()->toText());
         setNcrStatus(dhcp_ddns::ST_COMPLETED);
         setNcrStatus(dhcp_ddns::ST_COMPLETED);
         endModel();
         endModel();
         break;
         break;
@@ -565,9 +565,9 @@ NameRemoveTransaction::processRemoveFailedHandler() {
     case UPDATE_FAILED_EVT:
     case UPDATE_FAILED_EVT:
     case NO_MORE_SERVERS_EVT:
     case NO_MORE_SERVERS_EVT:
     case SERVER_IO_ERROR_EVT:
     case SERVER_IO_ERROR_EVT:
-        LOG_ERROR(dctl_logger, DHCP_DDNS_REMOVE_FAILED).arg(getNcr()->toText())
-        .arg(getEventLabel(getNextEvent()));
         setNcrStatus(dhcp_ddns::ST_FAILED);
         setNcrStatus(dhcp_ddns::ST_FAILED);
+        LOG_ERROR(dctl_logger, DHCP_DDNS_REMOVE_FAILED)
+                  .arg(transactionOutcomeString());
         endModel();
         endModel();
         break;
         break;
     default:
     default:

+ 64 - 4
src/bin/d2/nc_trans.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-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,8 @@
 #include <d2/nc_trans.h>
 #include <d2/nc_trans.h>
 #include <dns/rdata.h>
 #include <dns/rdata.h>
 
 
+#include <sstream>
+
 namespace isc {
 namespace isc {
 namespace d2 {
 namespace d2 {
 
 
@@ -94,18 +96,75 @@ NameChangeTransaction::operator()(DNSClient::Status status) {
     // set to indicate IO completed.
     // set to indicate IO completed.
     // runModel is exception safe so we are good to call it here.
     // runModel is exception safe so we are good to call it here.
     // It won't exit until we hit the next IO wait or the state model ends.
     // It won't exit until we hit the next IO wait or the state model ends.
+    setDnsUpdateStatus(status);
     LOG_DEBUG(dctl_logger, DBGLVL_TRACE_DETAIL,
     LOG_DEBUG(dctl_logger, DBGLVL_TRACE_DETAIL,
               DHCP_DDNS_UPDATE_RESPONSE_RECEIVED)
               DHCP_DDNS_UPDATE_RESPONSE_RECEIVED)
               .arg(getTransactionKey().toStr())
               .arg(getTransactionKey().toStr())
               .arg(current_server_->toText())
               .arg(current_server_->toText())
-              .arg(status);
+              .arg(responseString());
 
 
-    setDnsUpdateStatus(status);
     runModel(IO_COMPLETED_EVT);
     runModel(IO_COMPLETED_EVT);
 }
 }
 
 
+std::string
+NameChangeTransaction::responseString() const {
+    std::ostringstream stream;
+    switch (getDnsUpdateStatus()) {
+        case DNSClient::SUCCESS:
+            stream << "SUCCESS, rcode: ";
+            if (getDnsUpdateResponse()) {
+                 stream << getDnsUpdateResponse()->getRcode().toText();
+            } else {
+                stream << " update response is NULL";
+            }
+            break;
+        case DNSClient::TIMEOUT:
+            stream << "TIMEOUT";
+            break;
+        case DNSClient::IO_STOPPED:
+            stream << "IO_STOPPED";
+            break;
+        case DNSClient::INVALID_RESPONSE:
+            stream << "INVALID_RESPONSE";
+            break;
+        case DNSClient::OTHER:
+            stream << "OTHER";
+            break;
+        default:
+            stream << "UKNOWNN("
+                   << static_cast<int>(getDnsUpdateStatus()) << ")";
+            break;
+
+    }
+
+    return (stream.str());
+}
+
+std::string
+NameChangeTransaction::transactionOutcomeString() const {
+    std::ostringstream stream;
+    stream << "Status: " << (getNcrStatus() == dhcp_ddns::ST_COMPLETED
+                             ? "Completed, " : "Failed, ")
+           << "Event: " << getEventLabel(getNextEvent()) << ", ";
+
+    if (ncr_->isForwardChange()) {
+        stream << " Forward change:" << (getForwardChangeCompleted()
+                                         ? " completed, " : " failed, ");
+    }
+
+    if (ncr_->isReverseChange()) {
+        stream << " Reverse change:" << (getReverseChangeCompleted()
+                                          ? " completed, " : " failed, ");
+    }
+
+    stream << " request: " << ncr_->toText();
+    return (stream.str());
+}
+
+
 void
 void
-NameChangeTransaction::sendUpdate(bool /* use_tsig_ */) {
+NameChangeTransaction::sendUpdate(const std::string& comment,
+                                  bool /* use_tsig_ */) {
     try {
     try {
         ++update_attempts_;
         ++update_attempts_;
         // @todo add logic to add/replace TSIG key info in request if
         // @todo add logic to add/replace TSIG key info in request if
@@ -122,6 +181,7 @@ NameChangeTransaction::sendUpdate(bool /* use_tsig_ */) {
         postNextEvent(NOP_EVT);
         postNextEvent(NOP_EVT);
         LOG_DEBUG(dctl_logger, DBGLVL_TRACE_DETAIL,
         LOG_DEBUG(dctl_logger, DBGLVL_TRACE_DETAIL,
                   DHCP_DDNS_UPDATE_REQUEST_SENT)
                   DHCP_DDNS_UPDATE_REQUEST_SENT)
+                  .arg(comment)
                   .arg(getTransactionKey().toStr())
                   .arg(getTransactionKey().toStr())
                   .arg(current_server_->toText());
                   .arg(current_server_->toText());
     } catch (const std::exception& ex) {
     } catch (const std::exception& ex) {

+ 21 - 1
src/bin/d2/nc_trans.h

@@ -207,12 +207,14 @@ protected:
     /// currently selected server.  Since the send is asynchronous, the method
     /// currently selected server.  Since the send is asynchronous, the method
     /// posts NOP_EVT as the next event and then returns.
     /// posts NOP_EVT as the next event and then returns.
     ///
     ///
+    /// @param comment text to include in log detail
     /// @param use_tsig True if the update should be include a TSIG key. This
     /// @param use_tsig True if the update should be include a TSIG key. This
     /// is not yet implemented.
     /// is not yet implemented.
     ///
     ///
     /// If an exception occurs it will be logged and and the transaction will
     /// If an exception occurs it will be logged and and the transaction will
     /// be failed.
     /// be failed.
-    virtual void sendUpdate(bool use_tsig = false);
+    virtual void sendUpdate(const std::string& comment = "",
+                            bool use_tsig = false);
 
 
     /// @brief Adds events defined by NameChangeTransaction to the event set.
     /// @brief Adds events defined by NameChangeTransaction to the event set.
     ///
     ///
@@ -401,6 +403,24 @@ protected:
     /// the RData cannot be added to the given RRset.
     /// the RData cannot be added to the given RRset.
     void addPtrRdata(dns::RRsetPtr& rrset);
     void addPtrRdata(dns::RRsetPtr& rrset);
 
 
+    /// @brief Returns a string version of the current response status and rcode
+    ///
+    /// Renders a string containing the a text label current DNS update status
+    /// and RCODE (if status is DNSClient::SUCCESS)
+    ///
+    /// @return std::string containing constructed text
+    std::string responseString() const;
+
+    /// @brief Returns a string version of transaction outcome.
+    ///
+    /// Renders a string containing summarizes the outcome of the
+    /// transaction. The information includes the overall status,
+    /// the last event, whether not forward and reverse changes were
+    /// done, as well as the NCR serviced.
+    ///
+    /// @return std::string containing constructed text
+    std::string transactionOutcomeString() const;
+
 public:
 public:
     /// @brief Fetches the NameChangeRequest for this transaction.
     /// @brief Fetches the NameChangeRequest for this transaction.
     ///
     ///

+ 8 - 3
src/bin/d2/tests/d2_cfg_mgr_unittests.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-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
@@ -1066,6 +1066,10 @@ TEST_F(D2CfgMgrTest, forwardMatch) {
     EXPECT_TRUE(cfg_mgr_->matchForward("tmark.org", match));
     EXPECT_TRUE(cfg_mgr_->matchForward("tmark.org", match));
     EXPECT_EQ("tmark.org", match->getName());
     EXPECT_EQ("tmark.org", match->getName());
 
 
+    // Verify that search is case insensisitive.
+    EXPECT_TRUE(cfg_mgr_->matchForward("TMARK.ORG", match));
+    EXPECT_EQ("tmark.org", match->getName());
+
     // Verify that an exact match works.
     // Verify that an exact match works.
     EXPECT_TRUE(cfg_mgr_->matchForward("one.tmark.org", match));
     EXPECT_TRUE(cfg_mgr_->matchForward("one.tmark.org", match));
     EXPECT_EQ("one.tmark.org", match->getName());
     EXPECT_EQ("one.tmark.org", match->getName());
@@ -1207,7 +1211,8 @@ TEST_F(D2CfgMgrTest, matchReverse) {
                         "  \"dns_servers\" : [ "
                         "  \"dns_servers\" : [ "
                         "  { \"ip_address\": \"127.0.0.1\" } "
                         "  { \"ip_address\": \"127.0.0.1\" } "
                         "  ] }, "
                         "  ] }, "
-                        "{ \"name\": \"2.0.3.0.8.B.D.0.1.0.0.2.ip6.arpa.\" , "
+                        // Note mixed case to test case insensitivity.
+                        "{ \"name\": \"2.0.3.0.8.b.d.0.1.0.0.2.IP6.ARPA.\" , "
                         "  \"dns_servers\" : [ "
                         "  \"dns_servers\" : [ "
                         "  { \"ip_address\": \"127.0.0.1\" } "
                         "  { \"ip_address\": \"127.0.0.1\" } "
                         "  ] },"
                         "  ] },"
@@ -1247,7 +1252,7 @@ TEST_F(D2CfgMgrTest, matchReverse) {
 
 
     // Verify a IPv6 match.
     // Verify a IPv6 match.
     EXPECT_TRUE(cfg_mgr_->matchReverse("2001:db8:302:99::",match));
     EXPECT_TRUE(cfg_mgr_->matchReverse("2001:db8:302:99::",match));
-    EXPECT_EQ("2.0.3.0.8.B.D.0.1.0.0.2.ip6.arpa.", match->getName());
+    EXPECT_EQ("2.0.3.0.8.b.d.0.1.0.0.2.IP6.ARPA.", match->getName());
 
 
     // Verify a IPv6 wild card match.
     // Verify a IPv6 wild card match.
     EXPECT_TRUE(cfg_mgr_->matchReverse("2001:db8:99:302::",match));
     EXPECT_TRUE(cfg_mgr_->matchReverse("2001:db8:99:302::",match));

+ 4 - 3
src/bin/d2/tests/nc_add_unittests.cc

@@ -54,10 +54,11 @@ public:
     /// It will also simulate an exception-based failure of sendUpdate, if
     /// It will also simulate an exception-based failure of sendUpdate, if
     /// the simulate_send_exception_ flag is true.
     /// the simulate_send_exception_ flag is true.
     ///
     ///
-    /// @param use_tsig_ Parameter is unused, but present in the base class
-    /// method.
+    /// @param comment Parameter is unused, but present in base class method.
+    /// @param use_tsig_ Parameter is unused, but present in base class method.
     ///
     ///
-    virtual void sendUpdate(bool /* use_tsig_ = false */) {
+    virtual void sendUpdate(const std::string& /*comment*/,
+                            bool /* use_tsig_ = false */) {
         if (simulate_send_exception_) {
         if (simulate_send_exception_) {
             // Make the flag a one-shot by resetting it.
             // Make the flag a one-shot by resetting it.
             simulate_send_exception_ = false;
             simulate_send_exception_ = false;

+ 5 - 4
src/bin/d2/tests/nc_remove_unittests.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-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
@@ -55,10 +55,11 @@ public:
     /// It will also simulate an exception-based failure of sendUpdate, if
     /// It will also simulate an exception-based failure of sendUpdate, if
     /// the simulate_send_exception_ flag is true.
     /// the simulate_send_exception_ flag is true.
     ///
     ///
-    /// @param use_tsig_ Parameter is unused, but present in the base class
-    /// method.
+    /// @param comment Parameter is unused, but present in base class method
+    /// @param use_tsig Parameter is unused, but present in base class method.
     ///
     ///
-    virtual void sendUpdate(bool /* use_tsig_ = false */) {
+    virtual void sendUpdate(const std::string& /* comment */,
+                            bool /* use_tsig = false */) {
         if (simulate_send_exception_) {
         if (simulate_send_exception_) {
             // Make the flag a one-shot by resetting it.
             // Make the flag a one-shot by resetting it.
             simulate_send_exception_ = false;
             simulate_send_exception_ = false;

+ 81 - 0
src/bin/d2/tests/nc_trans_unittests.cc

@@ -262,6 +262,8 @@ public:
     using NameChangeTransaction::addLeaseAddressRdata;
     using NameChangeTransaction::addLeaseAddressRdata;
     using NameChangeTransaction::addDhcidRdata;
     using NameChangeTransaction::addDhcidRdata;
     using NameChangeTransaction::addPtrRdata;
     using NameChangeTransaction::addPtrRdata;
+    using NameChangeTransaction::responseString;
+    using NameChangeTransaction::transactionOutcomeString;
 };
 };
 
 
 // Declare them so Gtest can see them.
 // Declare them so Gtest can see them.
@@ -507,8 +509,87 @@ TEST_F(NameChangeTransactionTest, dnsUpdateResponseAccessors) {
 
 
     // Should be empty again.
     // Should be empty again.
     EXPECT_FALSE(name_change->getDnsUpdateResponse());
     EXPECT_FALSE(name_change->getDnsUpdateResponse());
+
+}
+
+/// @brief Tests responseString method.
+TEST_F(NameChangeTransactionTest, responseString) {
+    // Create a transaction.
+    NameChangeStubPtr name_change;
+    ASSERT_NO_THROW(name_change = makeCannedTransaction());
+
+    // Make sure it is safe to call when status says success but there
+    // is no update response.
+    ASSERT_NO_THROW(name_change->setDnsUpdateStatus(DNSClient::SUCCESS));
+    EXPECT_EQ("SUCCESS, rcode:  update response is NULL",
+              name_change->responseString());
+
+    // Create a response. (We use an OUTBOUND message so we can set RCODE)
+    D2UpdateMessagePtr resp;
+    ASSERT_NO_THROW(resp.reset(new D2UpdateMessage(D2UpdateMessage::OUTBOUND)));
+    ASSERT_NO_THROW(name_change->setDnsUpdateResponse(resp));
+
+    // Make sure we decode Rcode when status is successful.
+    ASSERT_NO_THROW(resp->setRcode(dns::Rcode::NXDOMAIN()));
+    EXPECT_EQ("SUCCESS, rcode: NXDOMAIN", name_change->responseString());
+
+    // Test all of the non-success values for status.
+    ASSERT_NO_THROW(name_change->setDnsUpdateStatus(DNSClient::TIMEOUT));
+    EXPECT_EQ("TIMEOUT", name_change->responseString());
+
+    ASSERT_NO_THROW(name_change->setDnsUpdateStatus(DNSClient::IO_STOPPED));
+    EXPECT_EQ("IO_STOPPED", name_change->responseString());
+
+    ASSERT_NO_THROW(name_change->setDnsUpdateStatus(DNSClient::
+                                                    INVALID_RESPONSE));
+    EXPECT_EQ("INVALID_RESPONSE", name_change->responseString());
+
+    ASSERT_NO_THROW(name_change->setDnsUpdateStatus(DNSClient::OTHER));
+    EXPECT_EQ("OTHER", name_change->responseString());
 }
 }
 
 
+/// @brief Tests transactionOutcomeString method.
+TEST_F(NameChangeTransactionTest, transactionOutcomeString) {
+    // Create a transaction.
+    NameChangeStubPtr name_change;
+    dhcp_ddns::NameChangeRequestPtr ncr;
+    ASSERT_NO_THROW(name_change = makeCannedTransaction());
+    ncr = name_change->getNcr();
+
+    // Check case of failed transaction in both directions
+    std::string exp_str("Status: Failed, Event: UNDEFINED,  Forward change:"
+                        " failed,  Reverse change: failed,  request: ");
+    exp_str += ncr->toText();
+
+    std::string tstring = name_change->transactionOutcomeString();
+    std::cout << "tstring is: [" << tstring << "]" << std::endl;
+
+    EXPECT_EQ(exp_str, name_change->transactionOutcomeString());
+
+    // Check case of success all around
+    name_change->setNcrStatus(dhcp_ddns::ST_COMPLETED);
+    name_change->setForwardChangeCompleted(true);
+    name_change->setReverseChangeCompleted(true);
+
+    exp_str = "Status: Completed, Event: UNDEFINED,  Forward change: completed,"
+              "  Reverse change: completed,  request: " + ncr->toText();
+    EXPECT_EQ(exp_str, name_change->transactionOutcomeString());
+
+    // Check case of success, with no forward change
+    name_change->setNcrStatus(dhcp_ddns::ST_COMPLETED);
+    ncr->setForwardChange(false);
+    exp_str = "Status: Completed, Event: UNDEFINED, "
+              " Reverse change: completed,  request: " + ncr->toText();
+    EXPECT_EQ(exp_str, name_change->transactionOutcomeString());
+
+    // Check case of success, with no reverse change
+    name_change->setNcrStatus(dhcp_ddns::ST_COMPLETED);
+    ncr->setForwardChange(true);
+    ncr->setReverseChange(false);
+    exp_str = "Status: Completed, Event: UNDEFINED, "
+              " Forward change: completed,  request: " + ncr->toText();
+    EXPECT_EQ(exp_str, name_change->transactionOutcomeString());
+}
 
 
 /// @brief Tests event and state dictionary construction and verification.
 /// @brief Tests event and state dictionary construction and verification.
 TEST_F(NameChangeTransactionTest, dictionaryCheck) {
 TEST_F(NameChangeTransactionTest, dictionaryCheck) {

+ 14 - 0
src/bin/dhcp6/ctrl_dhcp6_srv.cc

@@ -114,6 +114,16 @@ ControlledDhcpv6Srv::dhcp6ConfigHandler(ConstElementPtr new_config) {
         return (answer);
         return (answer);
     }
     }
 
 
+    // Server will start DDNS communications if its enabled.
+    try {
+        server_->startD2();
+    } catch (const std::exception& ex) {
+        std::ostringstream err;
+        err << "error starting DHCP_DDNS client "
+                " after server reconfiguration: " << ex.what();
+        return (isc::config::createAnswer(1, err.str()));
+    }
+
     // 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
     // sockets according to new configuration. This operation is not exception
     // safe and we really don't want to emit exceptions to the callback caller.
     // safe and we really don't want to emit exceptions to the callback caller.
@@ -212,6 +222,10 @@ void ControlledDhcpv6Srv::establishSession() {
     try {
     try {
         // Pull the full configuration out from the session.
         // Pull the full configuration out from the session.
         configureDhcp6Server(*this, config_session_->getFullConfig());
         configureDhcp6Server(*this, config_session_->getFullConfig());
+
+        // Server will start DDNS communications if its enabled.
+        server_->startD2();
+
         // 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.
         openActiveSockets(getPort());
         openActiveSockets(getPort());

+ 5 - 0
src/bin/dhcp6/dhcp6_messages.mes

@@ -91,6 +91,11 @@ New  values: hostname = %2, reverse mapping = %3, forward mapping = %4
 This debug message is logged when FQDN mapping for a particular lease has been
 This debug message is logged when FQDN mapping for a particular lease has been
 changed by the recent Renew message. This mapping will be changed in DNS.
 changed by the recent Renew message. This mapping will be changed in DNS.
 
 
+% DHCP6_DDNS_REQUEST_SEND_FAILED failed sending a request to b10-dhcp-ddns, error: %1,  ncr: %2
+This error message indicates that IPv6 DHCP server failed to send a DDNS
+update reqeust to the DHCP-DDNS server. This is most likely a configuration or
+networking error.
+
 % DHCP6_DEACTIVATE_INTERFACE deactivate interface %1
 % DHCP6_DEACTIVATE_INTERFACE deactivate interface %1
 This message is printed when DHCPv6 server disables an interface from being
 This message is printed when DHCPv6 server disables an interface from being
 used to receive DHCPv6 traffic. Sockets on this interface will not be opened
 used to receive DHCPv6 traffic. Sockets on this interface will not be opened

+ 44 - 32
src/bin/dhcp6/dhcp6_srv.cc

@@ -494,9 +494,6 @@ bool Dhcpv6Srv::run() {
                 LOG_ERROR(dhcp6_logger, DHCP6_PACKET_SEND_FAIL)
                 LOG_ERROR(dhcp6_logger, DHCP6_PACKET_SEND_FAIL)
                     .arg(e.what());
                     .arg(e.what());
             }
             }
-
-            // Send NameChangeRequests to the b10-dhcp-ddns module.
-            sendNameChangeRequests();
         }
         }
     }
     }
 
 
@@ -1077,18 +1074,19 @@ Dhcpv6Srv::createNameChangeRequests(const Pkt6Ptr& answer) {
         // Get the IP address from the lease. Also, use the S flag to determine
         // Get the IP address from the lease. Also, use the S flag to determine
         // if forward change should be performed. This flag will always be
         // if forward change should be performed. This flag will always be
         // set if server has taken responsibility for the forward update.
         // set if server has taken responsibility for the forward update.
-        NameChangeRequest ncr(isc::dhcp_ddns::CHG_ADD,
-                              opt_fqdn->getFlag(Option6ClientFqdn::FLAG_S),
-                              true, opt_fqdn->getDomainName(),
-                              iaaddr->getAddress().toText(),
-                              dhcid, 0, iaaddr->getValid());
-        // Add the request to the queue. This queue will be read elsewhere in
-        // the code and all requests from this queue will be sent to the
-        // D2 module.
-        name_change_reqs_.push(ncr);
+        NameChangeRequestPtr ncr;
+        ncr.reset(new NameChangeRequest(isc::dhcp_ddns::CHG_ADD,
+                                        opt_fqdn->getFlag(Option6ClientFqdn::
+                                                          FLAG_S),
+                                        true, opt_fqdn->getDomainName(),
+                                        iaaddr->getAddress().toText(),
+                                        dhcid, 0, iaaddr->getValid()));
 
 
         LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
         LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
-                  DHCP6_DDNS_CREATE_ADD_NAME_CHANGE_REQUEST).arg(ncr.toText());
+                  DHCP6_DDNS_CREATE_ADD_NAME_CHANGE_REQUEST).arg(ncr->toText());
+
+        // Post the NCR to the D2ClientMgr.
+        CfgMgr::instance().getD2ClientMgr().sendRequest(ncr);
 
 
         /// @todo Currently we create NCR with the first IPv6 address that
         /// @todo Currently we create NCR with the first IPv6 address that
         /// is carried in one of the IA_NAs. In the future, the NCR API should
         /// is carried in one of the IA_NAs. In the future, the NCR API should
@@ -1138,31 +1136,21 @@ Dhcpv6Srv::createRemovalNameChangeRequest(const Lease6Ptr& lease) {
 
 
     }
     }
     isc::dhcp_ddns::D2Dhcid dhcid(*lease->duid_, hostname_wire);
     isc::dhcp_ddns::D2Dhcid dhcid(*lease->duid_, hostname_wire);
-
     // Create a NameChangeRequest to remove the entry.
     // Create a NameChangeRequest to remove the entry.
-    NameChangeRequest ncr(isc::dhcp_ddns::CHG_REMOVE,
-                          lease->fqdn_fwd_, lease->fqdn_rev_,
-                          lease->hostname_,
-                          lease->addr_.toText(),
-                          dhcid, 0, lease->valid_lft_);
-    name_change_reqs_.push(ncr);
+    NameChangeRequestPtr ncr;
+    ncr.reset(new NameChangeRequest(isc::dhcp_ddns::CHG_REMOVE,
+                                    lease->fqdn_fwd_, lease->fqdn_rev_,
+                                    lease->hostname_,
+                                    lease->addr_.toText(),
+                                    dhcid, 0, lease->valid_lft_));
 
 
     LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
     LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
-              DHCP6_DDNS_CREATE_REMOVE_NAME_CHANGE_REQUEST).arg(ncr.toText());
-
-}
+              DHCP6_DDNS_CREATE_REMOVE_NAME_CHANGE_REQUEST).arg(ncr->toText());
 
 
-void
-Dhcpv6Srv::sendNameChangeRequests() {
-    while (!name_change_reqs_.empty()) {
-        // @todo Once next NameChangeRequest is picked from the queue
-        // we should send it to the b10-dhcp_ddns module. Currently we
-        // just drop it.
-        name_change_reqs_.pop();
-    }
+    // Post the NCR to the D2ClientMgr.
+    CfgMgr::instance().getD2ClientMgr().sendRequest(ncr);
 }
 }
 
 
-
 OptionPtr
 OptionPtr
 Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
 Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
                        const Pkt6Ptr& query, const Pkt6Ptr& answer,
                        const Pkt6Ptr& query, const Pkt6Ptr& answer,
@@ -2455,5 +2443,29 @@ Dhcpv6Srv::generateFqdn(const Pkt6Ptr& answer) {
     }
     }
 }
 }
 
 
+void
+Dhcpv6Srv::startD2() {
+    D2ClientMgr& d2_mgr = CfgMgr::instance().getD2ClientMgr();
+    if (d2_mgr.ddnsEnabled()) {
+        // Updates are enabled, so lets start the sender, passing in
+        // our error handler.
+        // This may throw so wherever this is called needs to ready.
+        d2_mgr.startSender(boost::bind(&Dhcpv6Srv::d2ClientErrorHandler,
+                                       this, _1, _2));
+    }
+}
+
+void
+Dhcpv6Srv::d2ClientErrorHandler(const
+                                dhcp_ddns::NameChangeSender::Result result,
+                                dhcp_ddns::NameChangeRequestPtr& ncr) {
+    LOG_ERROR(dhcp6_logger, DHCP6_DDNS_REQUEST_SEND_FAILED).
+              arg(result).arg((ncr ? ncr->toText() : " NULL "));
+    // We cannot communicate with b10-dhcp-ddns, suspend futher updates.
+    /// @todo We may wish to revisit this, but for now we will simpy turn
+    /// them off.
+    CfgMgr::instance().getD2ClientMgr().suspendUpdates();
+}
+
 };
 };
 };
 };

+ 26 - 11
src/bin/dhcp6/dhcp6_srv.h

@@ -24,6 +24,7 @@
 #include <dhcp/option_definition.h>
 #include <dhcp/option_definition.h>
 #include <dhcp/pkt6.h>
 #include <dhcp/pkt6.h>
 #include <dhcpsrv/alloc_engine.h>
 #include <dhcpsrv/alloc_engine.h>
+#include <dhcpsrv/d2_client_mgr.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/subnet.h>
 #include <hooks/callout_handle.h>
 #include <hooks/callout_handle.h>
 
 
@@ -117,6 +118,31 @@ public:
     /// @param port UDP port on which server should listen.
     /// @param port UDP port on which server should listen.
     static void openActiveSockets(const uint16_t port);
     static void openActiveSockets(const uint16_t port);
 
 
+    /// @brief Starts DHCP_DDNS client IO if DDNS updates are enabled.
+    ///
+    /// If updates are enabled, it Instructs the D2ClientMgr singleton to
+    /// enter send mode.  If D2ClientMgr encounters errors it may throw
+    /// D2ClientErrors. This method does not catch exceptions.
+    void startD2();
+
+    /// @brief Implements the error handler for DHCP_DDNS IO errors
+    ///
+    /// Invoked when a NameChangeRequest send to b10-dhcp-ddns completes with
+    /// a failed status.  These are communications errors, not data related
+    /// failures.
+    ///
+    /// This method logs the failure and then suspends all further updates.
+    /// Updating can only be restored by reconfiguration or restarting the
+    /// server.  There is currently no retry logic so the first IO error that
+    /// occurs will suspend updates.
+    /// @todo We may wish to make this more robust or sophisticated.
+    ///
+    /// @param result Result code of the send operation.
+    /// @param ncr NameChangeRequest which failed to send.
+    virtual void d2ClientErrorHandler(const dhcp_ddns::
+                                      NameChangeSender::Result result,
+                                      dhcp_ddns::NameChangeRequestPtr& ncr);
+
 protected:
 protected:
 
 
     /// @brief Compare received server id with our server id
     /// @brief Compare received server id with our server id
@@ -429,17 +455,6 @@ protected:
     /// records will be performed.
     /// records will be performed.
     void createRemovalNameChangeRequest(const Lease6Ptr& lease);
     void createRemovalNameChangeRequest(const Lease6Ptr& lease);
 
 
-    /// @brief Sends all outstanding NameChangeRequests to bind10-d2 module.
-    ///
-    /// The purpose of this function is to pick all outstanding
-    /// NameChangeRequests from the FIFO queue and send them to b10-dhcp-ddns
-    /// module.
-    ///
-    /// @todo Currently this function simply removes all requests from the
-    /// queue but doesn't send them anywhere. In the future, the
-    /// NameChangeSender will be used to deliver requests to the other module.
-    void sendNameChangeRequests();
-
     /// @brief Attempts to renew received addresses
     /// @brief Attempts to renew received addresses
     ///
     ///
     /// It iterates through received IA_NA options and attempts to renew
     /// It iterates through received IA_NA options and attempts to renew

+ 1 - 0
src/bin/dhcp6/tests/Makefile.am

@@ -75,6 +75,7 @@ dhcp6_unittests_SOURCES += hooks_unittest.cc
 dhcp6_unittests_SOURCES += dhcp6_test_utils.cc dhcp6_test_utils.h
 dhcp6_unittests_SOURCES += dhcp6_test_utils.cc dhcp6_test_utils.h
 dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc
 dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc
 dhcp6_unittests_SOURCES += config_parser_unittest.cc
 dhcp6_unittests_SOURCES += config_parser_unittest.cc
+dhcp6_unittests_SOURCES += d2_unittest.cc d2_unittest.h
 dhcp6_unittests_SOURCES += marker_file.cc
 dhcp6_unittests_SOURCES += marker_file.cc
 dhcp6_unittests_SOURCES += ../dhcp6_srv.h ../dhcp6_srv.cc
 dhcp6_unittests_SOURCES += ../dhcp6_srv.h ../dhcp6_srv.cc
 dhcp6_unittests_SOURCES += ../dhcp6_log.h ../dhcp6_log.cc
 dhcp6_unittests_SOURCES += ../dhcp6_log.h ../dhcp6_log.cc

+ 381 - 0
src/bin/dhcp6/tests/d2_unittest.cc

@@ -0,0 +1,381 @@
+// 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 <dhcp/iface_mgr.h>
+#include <dhcp6/config_parser.h>
+#include <dhcp6/tests/d2_unittest.h>
+#include <dhcpsrv/cfgmgr.h>
+
+#include <gtest/gtest.h>
+
+#include <string>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::data;
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+/// @todo
+void
+D2Dhcpv6Srv::d2ClientErrorHandler(const
+                                dhcp_ddns::NameChangeSender::Result result,
+                                dhcp_ddns::NameChangeRequestPtr& ncr) {
+    ++error_count_;
+    // call base class error handler
+    Dhcpv6Srv::d2ClientErrorHandler(result, ncr);
+}
+
+const bool Dhcp6SrvD2Test::SHOULD_PASS;
+const bool Dhcp6SrvD2Test::SHOULD_FAIL;
+
+Dhcp6SrvD2Test::Dhcp6SrvD2Test() : rcode_(-1) {
+}
+
+Dhcp6SrvD2Test::~Dhcp6SrvD2Test() {
+    reset();
+}
+
+dhcp_ddns::NameChangeRequestPtr
+Dhcp6SrvD2Test::buildTestNcr(uint32_t dhcid_id_num) {
+    // Build an NCR from json string.
+    std::ostringstream stream;
+
+    stream <<
+        "{"
+        " \"change_type\" : 0 , "
+        " \"forward_change\" : true , "
+        " \"reverse_change\" : false , "
+        " \"fqdn\" : \"myhost.example.com.\" , "
+        " \"ip_address\" : \"192.168.2.1\" , "
+        " \"dhcid\" : \""
+
+        << std::hex << std::setfill('0') << std::setw(16)
+        << dhcid_id_num << "\" , "
+
+        " \"lease_expires_on\" : \"20140121132405\" , "
+        " \"lease_length\" : 1300 "
+        "}";
+
+    return (dhcp_ddns::NameChangeRequest::fromJSON(stream.str()));
+}
+
+void
+Dhcp6SrvD2Test::reset() {
+    std::string config = "{ \"interfaces\": [ \"all\" ],"
+            "\"hooks-libraries\": [ ],"
+            "\"preferred-lifetime\": 3000,"
+            "\"rebind-timer\": 2000, "
+            "\"renew-timer\": 1000, "
+            "\"valid-lifetime\": 4000, "
+            "\"subnet6\": [ ], "
+            "\"dhcp-ddns\": { \"enable-updates\" : false }, "
+            "\"option-def\": [ ], "
+            "\"option-data\": [ ] }";
+    configure(config, SHOULD_PASS);
+}
+
+void
+Dhcp6SrvD2Test::configureD2(bool enable_d2, const bool exp_result,
+                            const std::string& ip_address,
+                            const uint32_t port) {
+    std::ostringstream config;
+    config <<
+        "{ \"interfaces\": [ \"*\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"pool\": [ \"2001:db8:1::1 - 2001:db8:1::ffff\" ],"
+        "    \"subnet\": \"2001:db8:1::/64\" } ],"
+        " \"dhcp-ddns\" : {"
+        "     \"enable-updates\" : " << (enable_d2 ? "true" : "false") <<  ", "
+        "     \"server-ip\" : \"" << ip_address << "\", "
+        "     \"server-port\" : " << port << ", "
+        "     \"ncr-protocol\" : \"UDP\", "
+        "     \"ncr-format\" : \"JSON\", "
+        "     \"always-include-fqdn\" : true, "
+        "     \"allow-client-update\" : true, "
+        "     \"override-no-update\" : true, "
+        "     \"override-client-update\" : true, "
+        "     \"replace-client-name\" : true, "
+        "     \"generated-prefix\" : \"test.prefix\", "
+        "     \"qualifying-suffix\" : \"test.suffix.\" },"
+        "\"valid-lifetime\": 4000 }";
+
+    configure(config.str(), exp_result);
+}
+
+void
+Dhcp6SrvD2Test::configure(const std::string& config, bool exp_result) {
+    ElementPtr json = Element::fromJSON(config);
+    ConstElementPtr status;
+
+    // Configure the server and make sure the config is accepted
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
+    ASSERT_TRUE(status);
+
+    int rcode;
+    ConstElementPtr comment = config::parseAnswer(rcode, status);
+    if (exp_result == SHOULD_PASS) {
+        ASSERT_EQ(0, rcode);
+    } else {
+        ASSERT_EQ(1, rcode);
+    }
+}
+
+// Tests ability to turn on and off ddns updates by submitting
+// by submitting the appropriate configuration to Dhcp6 server
+// and then invoking its startD2() method.
+TEST_F(Dhcp6SrvD2Test, enableDisable) {
+    // Grab the manager and verify that be default ddns is off
+    // and a sender was not started.
+    dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
+    ASSERT_FALSE(mgr.ddnsEnabled());
+    ASSERT_FALSE(mgr.amSending());
+
+    // Verify a valid config with ddns enabled configures ddns properly,
+    // but does not start the sender.
+    ASSERT_NO_FATAL_FAILURE(configureD2(true));
+    ASSERT_TRUE(mgr.ddnsEnabled());
+    ASSERT_FALSE(mgr.amSending());
+
+    // Verify that calling start does not throw and starts the sender.
+    ASSERT_NO_THROW(srv_.startD2());
+    ASSERT_TRUE(mgr.amSending());
+
+    // Verify a valid config with ddns disabled configures ddns properly.
+    // Sender should not have been started.
+    ASSERT_NO_FATAL_FAILURE(configureD2(false));
+    ASSERT_FALSE(mgr.ddnsEnabled());
+    ASSERT_FALSE(mgr.amSending());
+
+    // Verify that the sender does NOT get started when ddns is disabled.
+    srv_.startD2();
+    ASSERT_FALSE(mgr.amSending());
+}
+
+// Tests Dhcp6 server's ability to correctly handle a flawed dhcp-ddns configuration.
+// It does so by first enabling updates by submitting a valid configuration and then
+// ensuring they remain on after submitting a flawed configuration.
+// and then invoking its startD2() method.
+TEST_F(Dhcp6SrvD2Test, badConfig) {
+    // Grab the manager and verify that be default ddns is off
+    // and a sender was not started.
+    dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
+    ASSERT_FALSE(mgr.ddnsEnabled());
+
+    // Configure it enabled and start it.
+    ASSERT_NO_FATAL_FAILURE(configureD2(true));
+    ASSERT_TRUE(mgr.ddnsEnabled());
+    ASSERT_NO_THROW(srv_.startD2());
+    ASSERT_TRUE(mgr.amSending());
+
+    // Now attempt to give it an invalid configuration.
+    // Result should indicate failure.
+    ASSERT_NO_FATAL_FAILURE(configureD2(false, SHOULD_FAIL, "bogus_ip"));
+
+    // Configure was not altered, so ddns should be enabled and still sending.
+    ASSERT_TRUE(mgr.ddnsEnabled());
+    ASSERT_TRUE(mgr.amSending());
+
+    // Verify that calling start does not throw or stop the sender.
+    ASSERT_NO_THROW(srv_.startD2());
+    ASSERT_TRUE(mgr.amSending());
+
+}
+
+// Checks that submitting an identical dhcp-ddns configuration
+// is handled properly.  Not effect should be no change in
+// status for ddns updating.  Updates should still enabled and
+// in send mode.  This indicates that the sender was not stopped.
+TEST_F(Dhcp6SrvD2Test, sameConfig) {
+    // Grab the manager and verify that be default ddns is off
+    // and a sender was not started.
+    dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
+    ASSERT_FALSE(mgr.ddnsEnabled());
+
+    // Configure it enabled and start it.
+    ASSERT_NO_FATAL_FAILURE(configureD2(true));
+    ASSERT_TRUE(mgr.ddnsEnabled());
+    ASSERT_NO_THROW(srv_.startD2());
+    ASSERT_TRUE(mgr.amSending());
+
+    // Now submit an identical configuration.
+    ASSERT_NO_FATAL_FAILURE(configureD2(true));
+
+    // Configuration was not altered, so ddns should still enabled and sending.
+    ASSERT_TRUE(mgr.ddnsEnabled());
+    ASSERT_TRUE(mgr.amSending());
+
+    // Verify that calling start does not throw or stop the sender.
+    ASSERT_NO_THROW(srv_.startD2());
+    ASSERT_TRUE(mgr.amSending());
+}
+
+// Checks that submitting an different, but valid dhcp-ddns configuration
+// is handled properly.  Updates should be enabled, however they should
+// not yet be running.  This indicates that the sender was stopped and
+// replaced, but not yet started.
+TEST_F(Dhcp6SrvD2Test, differentConfig) {
+    // Grab the manager and verify that be default ddns is off
+    // and a sender was not started.
+    dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
+    ASSERT_FALSE(mgr.ddnsEnabled());
+
+    // Configure it enabled and start it.
+    ASSERT_NO_FATAL_FAILURE(configureD2(true));
+    ASSERT_TRUE(mgr.ddnsEnabled());
+    ASSERT_NO_THROW(srv_.startD2());
+    ASSERT_TRUE(mgr.amSending());
+
+    // Now enable it on a different port.
+    ASSERT_NO_FATAL_FAILURE(configureD2(true, SHOULD_PASS, "127.0.0.1", 54001));
+
+    // Configuration was altered, so ddns should still enabled but not sending.
+    ASSERT_TRUE(mgr.ddnsEnabled());
+    ASSERT_FALSE(mgr.amSending());
+
+    // Verify that calling start starts the sender.
+    ASSERT_NO_THROW(srv_.startD2());
+    ASSERT_TRUE(mgr.amSending());
+}
+
+// Checks that given a valid, enabled configuration and placing
+// sender in send mode, permits NCR requests to be sent via UPD
+// socket.  Note this test does not employ any sort of receiving
+// client to verify actual transmission.  These types of tests
+// are including under dhcp_ddns and d2 unit testing.
+TEST_F(Dhcp6SrvD2Test, simpleUDPSend) {
+    // Grab the manager and verify that be default ddns is off
+    // and a sender was not started.
+    dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
+    ASSERT_FALSE(mgr.ddnsEnabled());
+
+    // Configure it enabled and start it.
+    ASSERT_NO_FATAL_FAILURE(configureD2(true));
+    ASSERT_TRUE(mgr.ddnsEnabled());
+    ASSERT_NO_THROW(srv_.startD2());
+    ASSERT_TRUE(mgr.amSending());
+
+    // Verify that we can queue up a message.
+    dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr();
+    ASSERT_NO_THROW(mgr.sendRequest(ncr));
+    EXPECT_EQ(1, mgr.getQueueSize());
+
+    // Calling receive should detect the ready IO on the sender's select-fd,
+    // and invoke callback, which should complete the send.
+    ASSERT_NO_THROW(IfaceMgr::instance().receive4(0,0));
+
+    // Verify the queue is now empty.
+    EXPECT_EQ(0, mgr.getQueueSize());
+}
+
+// Checks that an IO error in sending a request to D2, results in ddns updates
+// being suspended.  This indicates that Dhcp6Srv's error handler has been
+// invoked as expected.  Note that this unit test relies on an attempt to send
+// to a server address of 0.0.0.0 port 0 fails under all OSs.
+TEST_F(Dhcp6SrvD2Test, forceUDPSendFailure) {
+    // Grab the manager and verify that be default ddns is off
+    // and a sender was not started.
+    dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
+    ASSERT_FALSE(mgr.ddnsEnabled());
+
+    // Configure it enabled and start it.
+    // Using server address of 0.0.0.0/0 should induce failure on send.
+    ASSERT_NO_FATAL_FAILURE(configureD2(true, SHOULD_PASS, "0.0.0.0", 0));
+    ASSERT_TRUE(mgr.ddnsEnabled());
+    ASSERT_NO_THROW(srv_.startD2());
+    ASSERT_TRUE(mgr.amSending());
+
+    // Queue up 3 messages.
+    for (int i = 0; i < 3; i++) {
+        dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr(i + 1);
+        ASSERT_NO_THROW(mgr.sendRequest(ncr));
+    }
+    EXPECT_EQ(3, mgr.getQueueSize());
+
+    // Calling receive should detect the ready IO on the sender's select-fd,
+    // and invoke callback, which should complete the send, which should
+    // fail.
+    ASSERT_NO_THROW(IfaceMgr::instance().receive4(0,0));
+
+    // Verify the error handler was invoked.
+    EXPECT_EQ(1, srv_.error_count_);
+
+    // Verify that updates are disabled and we are no longer sending.
+    ASSERT_FALSE(mgr.ddnsEnabled());
+    ASSERT_FALSE(mgr.amSending());
+
+    // Verify message is still in the queue.
+    EXPECT_EQ(3, mgr.getQueueSize());
+
+    // Verify that we can't just restart it.
+    /// @todo This may change if we add ability to resume.
+    ASSERT_NO_THROW(srv_.startD2());
+    ASSERT_FALSE(mgr.amSending());
+
+    // Configure it enabled and start it.
+    ASSERT_NO_FATAL_FAILURE(configureD2(true));
+    ASSERT_TRUE(mgr.ddnsEnabled());
+    ASSERT_NO_THROW(srv_.startD2());
+    ASSERT_TRUE(mgr.amSending());
+
+    // Verify message is still in the queue.
+    EXPECT_EQ(3, mgr.getQueueSize());
+
+    // This will finish sending the 1st message in queue
+    // and initiate send of 2nd message.
+    ASSERT_NO_THROW(IfaceMgr::instance().receive4(0,0));
+    EXPECT_EQ(1, srv_.error_count_);
+
+    // First message is off the queue.
+    EXPECT_EQ(2, mgr.getQueueSize());
+}
+
+// Tests error handling of D2ClientMgr::sendRequest() failure
+// by attempting to queue maximum number of messages.
+TEST_F(Dhcp6SrvD2Test, queueMaxError) {
+    // Configure it enabled and start it.
+    dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
+    ASSERT_NO_FATAL_FAILURE(configureD2(true));
+    ASSERT_TRUE(mgr.ddnsEnabled());
+    ASSERT_NO_THROW(srv_.startD2());
+    ASSERT_TRUE(mgr.amSending());
+
+    // Attempt to queue more then the maximum allowed.
+    int max_msgs = mgr.getQueueMaxSize();
+    for (int i = 0; i < max_msgs + 1; i++) {
+        dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr(i + 1);
+        ASSERT_NO_THROW(mgr.sendRequest(ncr));
+    }
+
+    // Stopping sender will complete the first message so there
+    // should be max less one.
+    EXPECT_EQ(max_msgs - 1, mgr.getQueueSize());
+
+    // Verify the error handler was invoked.
+    EXPECT_EQ(1, srv_.error_count_);
+
+    // Verify that updates are disabled and we are no longer sending.
+    ASSERT_FALSE(mgr.ddnsEnabled());
+    ASSERT_FALSE(mgr.amSending());
+}
+
+
+} // namespace test
+} // namespace dhcp
+} // namespace isc
+

+ 117 - 0
src/bin/dhcp6/tests/d2_unittest.h

@@ -0,0 +1,117 @@
+// 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.
+
+/// @file d2_unittest.h Defines classes for testing Dhcpv6srv with D2ClientMgr
+
+#ifndef D2_UNITTEST_H
+#define D2_UNITTEST_H
+
+#include <dhcp6/dhcp6_srv.h>
+#include <config/ccsession.h>
+
+#include <gtest/gtest.h>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+/// @brief Test derivation of Dhcpv6Srv class used in D2 testing.
+/// Use of this class allows the intervention at strategic points in testing
+/// by permitting overridden methods and access to scope protected members.
+class D2Dhcpv6Srv : public  Dhcpv6Srv {
+public:
+    /// @brief Counts the number of times the client error handler is called.
+    int error_count_;
+
+    /// @brief Constructor
+    D2Dhcpv6Srv()
+        : Dhcpv6Srv(0), error_count_(0) {
+    }
+
+    /// @brief virtual Destructor.
+    virtual ~D2Dhcpv6Srv() {
+    }
+
+    /// @brief Override the error handler.
+    virtual void d2ClientErrorHandler(const dhcp_ddns::NameChangeSender::
+                                      Result result,
+                                      dhcp_ddns::NameChangeRequestPtr& ncr);
+};
+
+/// @brief Test fixture which permits testing the interaction between the
+/// D2ClientMgr and Dhcpv6Srv.
+class Dhcp6SrvD2Test : public ::testing::Test {
+public:
+    /// @brief Mnemonic constants for calls to configuration methods.
+    static const bool SHOULD_PASS = true;
+    static const bool SHOULD_FAIL = false;
+
+    /// @brief Constructor
+    Dhcp6SrvD2Test();
+
+    /// @brief virtual Destructor
+    virtual ~Dhcp6SrvD2Test();
+
+    /// @brief Resets the CfgMgr singleton to defaults.
+    /// Primarily used in the test destructor as gtest doesn't exit between
+    /// tests.
+    /// @todo CfgMgr should provide a method to reset everything or maybe
+    /// reconstruct the singleton.
+    void reset();
+
+    /// @brief Configures the server with D2 enabled or disabled
+    ///
+    /// Constructs a configuration string including dhcp-ddns with the
+    /// parameters given and passes it into the server's configuration handler.
+    ///
+    /// @param enable_updates value to assign to the enable-updates parameter
+    /// @param exp_result indicates if configuration should pass or fail
+    /// @param ip_address IP address for the D2 server
+    /// @param port  port for the D2 server
+    void configureD2(bool enable_updates, bool exp_result = SHOULD_PASS,
+                     const std::string& ip_address = "127.0.0.1",
+                     const uint32_t port = 53001);
+
+    /// @brief Configures the server with the given configuration
+    ///
+    /// Passes the given configuration string into the server's configuration
+    /// handler.  It accepts a flag indicating whether or not the configuration
+    /// is expected to succeed or fail.  This permits testing the server's
+    /// response to both valid and invalid configurations.
+    ///
+    /// @param config JSON string containing the configuration
+    /// @param exp_result indicates if configuration should pass or fail
+    void configure(const std::string& config, bool exp_result = SHOULD_PASS);
+
+    /// @brief Contructs a NameChangeRequest message from a fixed JSON string.
+    ///
+    /// @param dhcid_id_num Integer value to use as the DHCID.
+    dhcp_ddns::NameChangeRequestPtr buildTestNcr(uint32_t
+                                                 dhcid_id_num = 0xdeadbeef);
+
+    /// @brief Stores the return code of the last configuration attempt.
+    int rcode_;
+
+    /// @brief Stores the message component of the last configuration tattempt.
+    isc::data::ConstElementPtr comment_;
+
+    /// @brief Server object under test.
+    D2Dhcpv6Srv srv_;
+};
+
+}; // end of isc::dhcp::test namespace
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif // D2_UNITTEST_H

+ 100 - 140
src/bin/dhcp6/tests/fqdn_unittest.cc

@@ -43,6 +43,9 @@ namespace {
 /// @brief A test fixture class for testing DHCPv6 Client FQDN Option handling.
 /// @brief A test fixture class for testing DHCPv6 Client FQDN Option handling.
 class FqdnDhcpv6SrvTest : public Dhcpv6SrvTest {
 class FqdnDhcpv6SrvTest : public Dhcpv6SrvTest {
 public:
 public:
+    /// Pointer to Dhcpv6Srv that is used in tests
+    boost::scoped_ptr<NakedDhcpv6Srv> srv_;
+
     // Reference to D2ClientMgr singleton
     // Reference to D2ClientMgr singleton
     D2ClientMgr& d2_mgr_;
     D2ClientMgr& d2_mgr_;
 
 
@@ -55,7 +58,8 @@ public:
 
 
     /// @brief Constructor
     /// @brief Constructor
     FqdnDhcpv6SrvTest()
     FqdnDhcpv6SrvTest()
-        : Dhcpv6SrvTest(), d2_mgr_(CfgMgr::instance().getD2ClientMgr()) {
+        : Dhcpv6SrvTest(), srv_(new NakedDhcpv6Srv(0)),
+          d2_mgr_(CfgMgr::instance().getD2ClientMgr()) {
         // generateClientId assigns DUID to duid_.
         // generateClientId assigns DUID to duid_.
         generateClientId();
         generateClientId();
         lease_.reset(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"),
         lease_.reset(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"),
@@ -100,6 +104,7 @@ public:
                                   (mask & REPLACE_CLIENT_NAME),
                                   (mask & REPLACE_CLIENT_NAME),
                                   "myhost", "example.com")));
                                   "myhost", "example.com")));
         ASSERT_NO_THROW(CfgMgr::instance().setD2ClientConfig(cfg));
         ASSERT_NO_THROW(CfgMgr::instance().setD2ClientConfig(cfg));
+        ASSERT_NO_THROW(srv_->startD2());
     }
     }
 
 
     /// @brief Construct the DHCPv6 Client FQDN option using flags and
     /// @brief Construct the DHCPv6 Client FQDN option using flags and
@@ -174,12 +179,9 @@ public:
     /// server id.
     /// server id.
     ///
     ///
     /// @param msg_type A type of the message to be created.
     /// @param msg_type A type of the message to be created.
-    /// @param srv An object representing the DHCPv6 server, which
-    /// is used to generate the client identifier.
     ///
     ///
     /// @return An object representing the created message.
     /// @return An object representing the created message.
-    Pkt6Ptr generateMessageWithIds(const uint8_t msg_type,
-                                   NakedDhcpv6Srv& srv) {
+    Pkt6Ptr generateMessageWithIds(const uint8_t msg_type) {
         Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(msg_type, 1234));
         Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(msg_type, 1234));
         // Generate client-id.
         // Generate client-id.
         OptionPtr opt_clientid = generateClientId();
         OptionPtr opt_clientid = generateClientId();
@@ -187,7 +189,7 @@ public:
 
 
         if (msg_type != DHCPV6_SOLICIT) {
         if (msg_type != DHCPV6_SOLICIT) {
             // Generate server-id.
             // Generate server-id.
-            pkt->addOption(srv.getServerID());
+            pkt->addOption(srv_->getServerID());
         }
         }
 
 
         return (pkt);
         return (pkt);
@@ -286,7 +288,7 @@ public:
                   const Option6ClientFqdn::DomainNameType in_domain_type,
                   const Option6ClientFqdn::DomainNameType in_domain_type,
                   const uint8_t exp_flags,
                   const uint8_t exp_flags,
                   const std::string& exp_domain_name) {
                   const std::string& exp_domain_name) {
-        NakedDhcpv6Srv srv(0);
+
         Pkt6Ptr question = generateMessage(msg_type,
         Pkt6Ptr question = generateMessage(msg_type,
                                            in_flags,
                                            in_flags,
                                            in_domain_name,
                                            in_domain_name,
@@ -296,7 +298,7 @@ public:
 
 
         Pkt6Ptr answer(new Pkt6(msg_type == DHCPV6_SOLICIT ? DHCPV6_ADVERTISE :
         Pkt6Ptr answer(new Pkt6(msg_type == DHCPV6_SOLICIT ? DHCPV6_ADVERTISE :
                                 DHCPV6_REPLY, question->getTransid()));
                                 DHCPV6_REPLY, question->getTransid()));
-        ASSERT_NO_THROW(srv.processClientFqdn(question, answer));
+        ASSERT_NO_THROW(srv_->processClientFqdn(question, answer));
         Option6ClientFqdnPtr answ_fqdn = boost::dynamic_pointer_cast<
         Option6ClientFqdnPtr answ_fqdn = boost::dynamic_pointer_cast<
             Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
             Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
         ASSERT_TRUE(answ_fqdn);
         ASSERT_TRUE(answ_fqdn);
@@ -330,7 +332,6 @@ public:
     ///
     ///
     /// @param msg_type A type of the client's message.
     /// @param msg_type A type of the client's message.
     /// @param hostname A domain name in the client's FQDN.
     /// @param hostname A domain name in the client's FQDN.
-    /// @param srv A server object, used to process the message.
     /// @param include_oro A boolean value which indicates whether the ORO
     /// @param include_oro A boolean value which indicates whether the ORO
     /// option should be included in the client's message (if true) or not
     /// option should be included in the client's message (if true) or not
     /// (if false). In the former case, the function will expect that server
     /// (if false). In the former case, the function will expect that server
@@ -339,11 +340,10 @@ public:
     void testProcessMessage(const uint8_t msg_type,
     void testProcessMessage(const uint8_t msg_type,
                             const std::string& hostname,
                             const std::string& hostname,
                             const std::string& exp_hostname,
                             const std::string& exp_hostname,
-                            NakedDhcpv6Srv& srv,
                             const bool include_oro = true) {
                             const bool include_oro = true) {
         // Create a message of a specified type, add server id and
         // Create a message of a specified type, add server id and
         // FQDN option.
         // FQDN option.
-        OptionPtr srvid = srv.getServerID();
+        OptionPtr srvid = srv_->getServerID();
         // Set the appropriate FQDN type. It must be partial if hostname is
         // Set the appropriate FQDN type. It must be partial if hostname is
         // empty.
         // empty.
         Option6ClientFqdn::DomainNameType fqdn_type = (hostname.empty() ?
         Option6ClientFqdn::DomainNameType fqdn_type = (hostname.empty() ?
@@ -356,18 +356,18 @@ public:
         // functions to generate response.
         // functions to generate response.
         Pkt6Ptr reply;
         Pkt6Ptr reply;
         if (msg_type == DHCPV6_SOLICIT) {
         if (msg_type == DHCPV6_SOLICIT) {
-            ASSERT_NO_THROW(reply = srv.processSolicit(req));
+            ASSERT_NO_THROW(reply = srv_->processSolicit(req));
 
 
         } else if (msg_type == DHCPV6_REQUEST) {
         } else if (msg_type == DHCPV6_REQUEST) {
-            ASSERT_NO_THROW(reply = srv.processRequest(req));
+            ASSERT_NO_THROW(reply = srv_->processRequest(req));
 
 
         } else if (msg_type == DHCPV6_RENEW) {
         } else if (msg_type == DHCPV6_RENEW) {
-            ASSERT_NO_THROW(reply = srv.processRequest(req));
+            ASSERT_NO_THROW(reply = srv_->processRequest(req));
 
 
         } else if (msg_type == DHCPV6_RELEASE) {
         } else if (msg_type == DHCPV6_RELEASE) {
             // For Release no lease will be acquired so we have to leave
             // For Release no lease will be acquired so we have to leave
             // function here.
             // function here.
-            ASSERT_NO_THROW(reply = srv.processRelease(req));
+            ASSERT_NO_THROW(reply = srv_->processRelease(req));
             return;
             return;
         } else {
         } else {
             // We are not interested in testing other message types.
             // We are not interested in testing other message types.
@@ -413,7 +413,6 @@ public:
     /// queue and checks that it holds valid parameters. The NameChangeRequest
     /// queue and checks that it holds valid parameters. The NameChangeRequest
     /// is removed from the queue.
     /// is removed from the queue.
     ///
     ///
-    /// @param srv A server object holding a queue of NameChangeRequests.
     /// @param type An expected type of the NameChangeRequest (Add or Remove).
     /// @param type An expected type of the NameChangeRequest (Add or Remove).
     /// @param reverse An expected setting of the reverse update flag.
     /// @param reverse An expected setting of the reverse update flag.
     /// @param forward An expected setting of the forward udpate flag.
     /// @param forward An expected setting of the forward udpate flag.
@@ -431,23 +430,27 @@ public:
     /// NameChangeRequest expires.
     /// NameChangeRequest expires.
     /// @param len A valid lifetime of the lease associated with the
     /// @param len A valid lifetime of the lease associated with the
     /// NameChangeRequest.
     /// NameChangeRequest.
-    void verifyNameChangeRequest(NakedDhcpv6Srv& srv,
-                                 const isc::dhcp_ddns::NameChangeType type,
+    void verifyNameChangeRequest(const isc::dhcp_ddns::NameChangeType type,
                                  const bool reverse, const bool forward,
                                  const bool reverse, const bool forward,
                                  const std::string& addr,
                                  const std::string& addr,
                                  const std::string& dhcid,
                                  const std::string& dhcid,
                                  const uint16_t expires,
                                  const uint16_t expires,
                                  const uint16_t len) {
                                  const uint16_t len) {
-        NameChangeRequest ncr = srv.name_change_reqs_.front();
-        EXPECT_EQ(type, ncr.getChangeType());
-        EXPECT_EQ(forward, ncr.isForwardChange());
-        EXPECT_EQ(reverse, ncr.isReverseChange());
-        EXPECT_EQ(addr, ncr.getIpAddress());
-        EXPECT_EQ(dhcid, ncr.getDhcid().toStr());
-        EXPECT_EQ(expires, ncr.getLeaseExpiresOn());
-        EXPECT_EQ(len, ncr.getLeaseLength());
-        EXPECT_EQ(isc::dhcp_ddns::ST_NEW, ncr.getStatus());
-        srv.name_change_reqs_.pop();
+        NameChangeRequestPtr ncr;
+        ASSERT_NO_THROW(ncr = d2_mgr_.peekAt(0));
+        ASSERT_TRUE(ncr);
+
+        EXPECT_EQ(type, ncr->getChangeType());
+        EXPECT_EQ(forward, ncr->isForwardChange());
+        EXPECT_EQ(reverse, ncr->isReverseChange());
+        EXPECT_EQ(addr, ncr->getIpAddress());
+        EXPECT_EQ(dhcid, ncr->getDhcid().toStr());
+        EXPECT_EQ(expires, ncr->getLeaseExpiresOn());
+        EXPECT_EQ(len, ncr->getLeaseLength());
+        EXPECT_EQ(isc::dhcp_ddns::ST_NEW, ncr->getStatus());
+
+        // Process the message off the queue
+        ASSERT_NO_THROW(d2_mgr_.runReadyIO());
     }
     }
 
 
     // Holds a lease used by a test.
     // Holds a lease used by a test.
@@ -504,11 +507,9 @@ TEST_F(FqdnDhcpv6SrvTest, clientAAAAUpdateNotAllowed) {
 // Test that exception is thrown if supplied NULL answer packet when
 // Test that exception is thrown if supplied NULL answer packet when
 // creating NameChangeRequests.
 // creating NameChangeRequests.
 TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoAnswer) {
 TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoAnswer) {
-    NakedDhcpv6Srv srv(0);
-
     Pkt6Ptr answer;
     Pkt6Ptr answer;
 
 
-    EXPECT_THROW(srv.createNameChangeRequests(answer),
+    EXPECT_THROW(srv_->createNameChangeRequests(answer),
                  isc::Unexpected);
                  isc::Unexpected);
 
 
 }
 }
@@ -516,39 +517,33 @@ TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoAnswer) {
 // Test that exception is thrown if supplied answer from the server
 // Test that exception is thrown if supplied answer from the server
 // contains no DUID when creating NameChangeRequests.
 // contains no DUID when creating NameChangeRequests.
 TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoDUID) {
 TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoDUID) {
-    NakedDhcpv6Srv srv(0);
-
     Pkt6Ptr answer = Pkt6Ptr(new Pkt6(DHCPV6_REPLY, 1234));
     Pkt6Ptr answer = Pkt6Ptr(new Pkt6(DHCPV6_REPLY, 1234));
     Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
     Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
                                                  "myhost.example.com",
                                                  "myhost.example.com",
                                                  Option6ClientFqdn::FULL);
                                                  Option6ClientFqdn::FULL);
     answer->addOption(fqdn);
     answer->addOption(fqdn);
 
 
-    EXPECT_THROW(srv.createNameChangeRequests(answer), isc::Unexpected);
+    EXPECT_THROW(srv_->createNameChangeRequests(answer), isc::Unexpected);
 
 
 }
 }
 
 
 // Test no NameChangeRequests if Client FQDN is not added to the server's
 // Test no NameChangeRequests if Client FQDN is not added to the server's
 // response.
 // response.
 TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoFQDN) {
 TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoFQDN) {
-    NakedDhcpv6Srv srv(0);
-
     // Create Reply message with Client Id and Server id.
     // Create Reply message with Client Id and Server id.
-    Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
+    Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY);
 
 
-    ASSERT_NO_THROW(srv.createNameChangeRequests(answer));
+    ASSERT_NO_THROW(srv_->createNameChangeRequests(answer));
 
 
     // There should be no new NameChangeRequests.
     // There should be no new NameChangeRequests.
-    EXPECT_TRUE(srv.name_change_reqs_.empty());
+    ASSERT_EQ(0, d2_mgr_.getQueueSize());
 }
 }
 
 
 // Test that NameChangeRequests are not generated if an answer message
 // Test that NameChangeRequests are not generated if an answer message
 // contains no addresses.
 // contains no addresses.
 TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoAddr) {
 TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoAddr) {
-    NakedDhcpv6Srv srv(0);
-
     // Create Reply message with Client Id and Server id.
     // Create Reply message with Client Id and Server id.
-    Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
+    Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY);
 
 
     // Add Client FQDN option.
     // Add Client FQDN option.
     Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
     Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
@@ -556,20 +551,18 @@ TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoAddr) {
                                                  Option6ClientFqdn::FULL);
                                                  Option6ClientFqdn::FULL);
     answer->addOption(fqdn);
     answer->addOption(fqdn);
 
 
-    ASSERT_NO_THROW(srv.createNameChangeRequests(answer));
+    ASSERT_NO_THROW(srv_->createNameChangeRequests(answer));
 
 
     // We didn't add any IAs, so there should be no NameChangeRequests in th
     // We didn't add any IAs, so there should be no NameChangeRequests in th
     // queue.
     // queue.
-    ASSERT_TRUE(srv.name_change_reqs_.empty());
+    ASSERT_EQ(0, d2_mgr_.getQueueSize());
 }
 }
 
 
 // Test that exactly one NameChangeRequest is created as a result of processing
 // Test that exactly one NameChangeRequest is created as a result of processing
 // the answer message which holds 3 IAs and when FQDN is specified.
 // the answer message which holds 3 IAs and when FQDN is specified.
 TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequests) {
 TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequests) {
-    NakedDhcpv6Srv srv(0);
-
     // Create Reply message with Client Id and Server id.
     // Create Reply message with Client Id and Server id.
-    Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
+    Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY);
 
 
     // Create three IAs, each having different address.
     // Create three IAs, each having different address.
     addIA(1234, IOAddress("2001:db8:1::1"), answer);
     addIA(1234, IOAddress("2001:db8:1::1"), answer);
@@ -585,11 +578,11 @@ TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequests) {
     answer->addOption(fqdn);
     answer->addOption(fqdn);
 
 
     // Create NameChangeRequest for the first allocated address.
     // Create NameChangeRequest for the first allocated address.
-    ASSERT_NO_THROW(srv.createNameChangeRequests(answer));
-    ASSERT_EQ(1, srv.name_change_reqs_.size());
+    ASSERT_NO_THROW(srv_->createNameChangeRequests(answer));
+    ASSERT_EQ(1, d2_mgr_.getQueueSize());
 
 
     // Verify that NameChangeRequest is correct.
     // Verify that NameChangeRequest is correct.
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
                             "2001:db8:1::1",
                             "2001:db8:1::1",
                             "000201415AA33D1187D148275136FA30300478"
                             "000201415AA33D1187D148275136FA30300478"
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
@@ -600,13 +593,11 @@ TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequests) {
 // Checks that NameChangeRequests to add entries are not
 // Checks that NameChangeRequests to add entries are not
 // created when ddns updates are disabled.
 // created when ddns updates are disabled.
 TEST_F(FqdnDhcpv6SrvTest, noAddRequestsWhenDisabled) {
 TEST_F(FqdnDhcpv6SrvTest, noAddRequestsWhenDisabled) {
-    NakedDhcpv6Srv srv(0);
-
     // Disable DDNS udpates.
     // Disable DDNS udpates.
     disableD2();
     disableD2();
 
 
     // Create Reply message with Client Id and Server id.
     // Create Reply message with Client Id and Server id.
-    Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
+    Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY);
 
 
     // Create three IAs, each having different address.
     // Create three IAs, each having different address.
     addIA(1234, IOAddress("2001:db8:1::1"), answer);
     addIA(1234, IOAddress("2001:db8:1::1"), answer);
@@ -619,17 +610,14 @@ TEST_F(FqdnDhcpv6SrvTest, noAddRequestsWhenDisabled) {
                                                  Option6ClientFqdn::FULL);
                                                  Option6ClientFqdn::FULL);
     answer->addOption(fqdn);
     answer->addOption(fqdn);
 
 
-    // Create NameChangeRequest for the first allocated address.
-    ASSERT_NO_THROW(srv.createNameChangeRequests(answer));
-    ASSERT_TRUE(srv.name_change_reqs_.empty());
+    // An attempt to send a NCR would throw.
+    ASSERT_NO_THROW(srv_->createNameChangeRequests(answer));
 }
 }
 
 
 
 
 // Test creation of the NameChangeRequest to remove both forward and reverse
 // Test creation of the NameChangeRequest to remove both forward and reverse
 // mapping for the given lease.
 // mapping for the given lease.
 TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestFwdRev) {
 TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestFwdRev) {
-    NakedDhcpv6Srv srv(0);
-
     lease_->fqdn_fwd_ = true;
     lease_->fqdn_fwd_ = true;
     lease_->fqdn_rev_ = true;
     lease_->fqdn_rev_ = true;
     // Part of the domain name is in upper case, to test that it gets converted
     // Part of the domain name is in upper case, to test that it gets converted
@@ -637,11 +625,10 @@ TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestFwdRev) {
     // as if we typed domain-name in lower case.
     // as if we typed domain-name in lower case.
     lease_->hostname_ = "MYHOST.example.com.";
     lease_->hostname_ = "MYHOST.example.com.";
 
 
-    ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
-
-    ASSERT_EQ(1, srv.name_change_reqs_.size());
+    ASSERT_NO_THROW(srv_->createRemovalNameChangeRequest(lease_));
 
 
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
+    ASSERT_EQ(1, d2_mgr_.getQueueSize());
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE, true, true,
                             "2001:db8:1::1",
                             "2001:db8:1::1",
                             "000201415AA33D1187D148275136FA30300478"
                             "000201415AA33D1187D148275136FA30300478"
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
@@ -652,8 +639,6 @@ TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestFwdRev) {
 // Checks that NameChangeRequests to remove entries are not created
 // Checks that NameChangeRequests to remove entries are not created
 // when ddns updates are disabled.
 // when ddns updates are disabled.
 TEST_F(FqdnDhcpv6SrvTest, noRemovalsWhenDisabled) {
 TEST_F(FqdnDhcpv6SrvTest, noRemovalsWhenDisabled) {
-    NakedDhcpv6Srv srv(0);
-
     // Disable DDNS updates.
     // Disable DDNS updates.
     disableD2();
     disableD2();
 
 
@@ -664,26 +649,23 @@ TEST_F(FqdnDhcpv6SrvTest, noRemovalsWhenDisabled) {
     // as if we typed domain-name in lower case.
     // as if we typed domain-name in lower case.
     lease_->hostname_ = "MYHOST.example.com.";
     lease_->hostname_ = "MYHOST.example.com.";
 
 
-    ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
-
-    ASSERT_TRUE(srv.name_change_reqs_.empty());
+    // When DDNS is disabled an attempt to send a request will throw.
+    ASSERT_NO_THROW(srv_->createRemovalNameChangeRequest(lease_));
 }
 }
 
 
 
 
 // Test creation of the NameChangeRequest to remove reverse mapping for the
 // Test creation of the NameChangeRequest to remove reverse mapping for the
 // given lease.
 // given lease.
 TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestRev) {
 TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestRev) {
-    NakedDhcpv6Srv srv(0);
-
     lease_->fqdn_fwd_ = false;
     lease_->fqdn_fwd_ = false;
     lease_->fqdn_rev_ = true;
     lease_->fqdn_rev_ = true;
     lease_->hostname_ = "myhost.example.com.";
     lease_->hostname_ = "myhost.example.com.";
 
 
-    ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
+    ASSERT_NO_THROW(srv_->createRemovalNameChangeRequest(lease_));
 
 
-    ASSERT_EQ(1, srv.name_change_reqs_.size());
+    ASSERT_EQ(1, d2_mgr_.getQueueSize());
 
 
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, false,
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE, true, false,
                             "2001:db8:1::1",
                             "2001:db8:1::1",
                             "000201415AA33D1187D148275136FA30300478"
                             "000201415AA33D1187D148275136FA30300478"
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
@@ -694,29 +676,25 @@ TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestRev) {
 // Test that NameChangeRequest to remove DNS records is not generated when
 // Test that NameChangeRequest to remove DNS records is not generated when
 // neither forward nor reverse DNS update has been performed for a lease.
 // neither forward nor reverse DNS update has been performed for a lease.
 TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestNoUpdate) {
 TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestNoUpdate) {
-    NakedDhcpv6Srv srv(0);
-
     lease_->fqdn_fwd_ = false;
     lease_->fqdn_fwd_ = false;
     lease_->fqdn_rev_ = false;
     lease_->fqdn_rev_ = false;
 
 
-    ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
+    ASSERT_NO_THROW(srv_->createRemovalNameChangeRequest(lease_));
 
 
-    EXPECT_TRUE(srv.name_change_reqs_.empty());
+    ASSERT_EQ(0, d2_mgr_.getQueueSize());
 
 
 }
 }
 
 
 // Test that NameChangeRequest is not generated if the hostname hasn't been
 // Test that NameChangeRequest is not generated if the hostname hasn't been
 // specified for a lease for which forward and reverse mapping has been set.
 // specified for a lease for which forward and reverse mapping has been set.
 TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestNoHostname) {
 TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestNoHostname) {
-    NakedDhcpv6Srv srv(0);
-
     lease_->fqdn_fwd_ = true;
     lease_->fqdn_fwd_ = true;
     lease_->fqdn_rev_ = true;
     lease_->fqdn_rev_ = true;
     lease_->hostname_ = "";
     lease_->hostname_ = "";
 
 
-    ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
+    ASSERT_NO_THROW(srv_->createRemovalNameChangeRequest(lease_));
 
 
-    EXPECT_TRUE(srv.name_change_reqs_.empty());
+    ASSERT_EQ(0, d2_mgr_.getQueueSize());
 
 
 }
 }
 
 
@@ -724,28 +702,24 @@ TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestNoHostname) {
 // been specified for a lease for which forward and reverse mapping has been
 // been specified for a lease for which forward and reverse mapping has been
 // set.
 // set.
 TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestWrongHostname) {
 TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestWrongHostname) {
-    NakedDhcpv6Srv srv(0);
-
     lease_->fqdn_fwd_ = true;
     lease_->fqdn_fwd_ = true;
     lease_->fqdn_rev_ = true;
     lease_->fqdn_rev_ = true;
     lease_->hostname_ = "myhost..example.com.";
     lease_->hostname_ = "myhost..example.com.";
 
 
-    ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
+    ASSERT_NO_THROW(srv_->createRemovalNameChangeRequest(lease_));
 
 
-    EXPECT_TRUE(srv.name_change_reqs_.empty());
+    ASSERT_EQ(0, d2_mgr_.getQueueSize());
 
 
 }
 }
 
 
 // Test that Advertise message generated in a response to the Solicit will
 // Test that Advertise message generated in a response to the Solicit will
 // not result in generation if the NameChangeRequests.
 // not result in generation if the NameChangeRequests.
 TEST_F(FqdnDhcpv6SrvTest, processSolicit) {
 TEST_F(FqdnDhcpv6SrvTest, processSolicit) {
-    NakedDhcpv6Srv srv(0);
-
     // Create a Solicit message with FQDN option and generate server's
     // Create a Solicit message with FQDN option and generate server's
     // response using processSolicit function.
     // response using processSolicit function.
     testProcessMessage(DHCPV6_SOLICIT, "myhost.example.com",
     testProcessMessage(DHCPV6_SOLICIT, "myhost.example.com",
-                       "myhost.example.com.", srv);
-    EXPECT_TRUE(srv.name_change_reqs_.empty());
+                       "myhost.example.com.");
+    ASSERT_EQ(0, d2_mgr_.getQueueSize());
 }
 }
 
 
 // Test that client may send two requests, each carrying FQDN option with
 // Test that client may send two requests, each carrying FQDN option with
@@ -753,16 +727,14 @@ TEST_F(FqdnDhcpv6SrvTest, processSolicit) {
 // request but modify the DNS entries for the lease according to the contents
 // request but modify the DNS entries for the lease according to the contents
 // of the FQDN sent in the second request.
 // of the FQDN sent in the second request.
 TEST_F(FqdnDhcpv6SrvTest, processTwoRequests) {
 TEST_F(FqdnDhcpv6SrvTest, processTwoRequests) {
-    NakedDhcpv6Srv srv(0);
-
     // Create a Request message with FQDN option and generate server's
     // Create a Request message with FQDN option and generate server's
     // response using processRequest function. This will result in the
     // response using processRequest function. This will result in the
     // creation of a new lease and the appropriate NameChangeRequest
     // creation of a new lease and the appropriate NameChangeRequest
     // to add both reverse and forward mapping to DNS.
     // to add both reverse and forward mapping to DNS.
     testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
     testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
-                       "myhost.example.com.", srv);
-    ASSERT_EQ(1, srv.name_change_reqs_.size());
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+                       "myhost.example.com.");
+    ASSERT_EQ(1, d2_mgr_.getQueueSize());
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
                             "2001:db8:1:1::dead:beef",
                             "2001:db8:1:1::dead:beef",
                             "000201415AA33D1187D148275136FA30300478"
                             "000201415AA33D1187D148275136FA30300478"
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
@@ -776,14 +748,14 @@ TEST_F(FqdnDhcpv6SrvTest, processTwoRequests) {
     // should be added. Therefore, we expect two NameChangeRequests. One to
     // should be added. Therefore, we expect two NameChangeRequests. One to
     // remove the existing entries, one to add new entries.
     // remove the existing entries, one to add new entries.
     testProcessMessage(DHCPV6_REQUEST, "otherhost.example.com",
     testProcessMessage(DHCPV6_REQUEST, "otherhost.example.com",
-                       "otherhost.example.com.", srv);
-    ASSERT_EQ(2, srv.name_change_reqs_.size());
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
+                       "otherhost.example.com.");
+    ASSERT_EQ(2, d2_mgr_.getQueueSize());
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE, true, true,
                             "2001:db8:1:1::dead:beef",
                             "2001:db8:1:1::dead:beef",
                             "000201415AA33D1187D148275136FA30300478"
                             "000201415AA33D1187D148275136FA30300478"
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
                             0, 4000);
                             0, 4000);
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
                             "2001:db8:1:1::dead:beef",
                             "2001:db8:1:1::dead:beef",
                             "000201D422AA463306223D269B6CB7AFE7AAD265FC"
                             "000201D422AA463306223D269B6CB7AFE7AAD265FC"
                             "EA97F93623019B2E0D14E5323D5A",
                             "EA97F93623019B2E0D14E5323D5A",
@@ -797,16 +769,14 @@ TEST_F(FqdnDhcpv6SrvTest, processTwoRequests) {
 // DNS if the Request was sent instead of Soicit. The code should differentiate
 // DNS if the Request was sent instead of Soicit. The code should differentiate
 // behavior depending whether Solicit or Request is sent.
 // behavior depending whether Solicit or Request is sent.
 TEST_F(FqdnDhcpv6SrvTest, processRequestSolicit) {
 TEST_F(FqdnDhcpv6SrvTest, processRequestSolicit) {
-    NakedDhcpv6Srv srv(0);
-
     // Create a Request message with FQDN option and generate server's
     // Create a Request message with FQDN option and generate server's
     // response using processRequest function. This will result in the
     // response using processRequest function. This will result in the
     // creation of a new lease and the appropriate NameChangeRequest
     // creation of a new lease and the appropriate NameChangeRequest
     // to add both reverse and forward mapping to DNS.
     // to add both reverse and forward mapping to DNS.
     testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
     testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
-                       "myhost.example.com.", srv);
-    ASSERT_EQ(1, srv.name_change_reqs_.size());
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+                       "myhost.example.com.");
+    ASSERT_EQ(1, d2_mgr_.getQueueSize());
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
                             "2001:db8:1:1::dead:beef",
                             "2001:db8:1:1::dead:beef",
                             "000201415AA33D1187D148275136FA30300478"
                             "000201415AA33D1187D148275136FA30300478"
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
@@ -817,8 +787,8 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestSolicit) {
     // The NameChangeRequest should only be generated when a client sends
     // The NameChangeRequest should only be generated when a client sends
     // Request or Renew.
     // Request or Renew.
     testProcessMessage(DHCPV6_SOLICIT, "otherhost.example.com",
     testProcessMessage(DHCPV6_SOLICIT, "otherhost.example.com",
-                       "otherhost.example.com.", srv);
-    ASSERT_TRUE(srv.name_change_reqs_.empty());
+                       "otherhost.example.com.");
+    ASSERT_EQ(0, d2_mgr_.getQueueSize());
 
 
 }
 }
 
 
@@ -829,16 +799,14 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestSolicit) {
 // DNS entry added previously when Request was processed, another one to
 // DNS entry added previously when Request was processed, another one to
 // add a new entry for the FQDN held in the Renew.
 // add a new entry for the FQDN held in the Renew.
 TEST_F(FqdnDhcpv6SrvTest, processRequestRenew) {
 TEST_F(FqdnDhcpv6SrvTest, processRequestRenew) {
-    NakedDhcpv6Srv srv(0);
-
     // Create a Request message with FQDN option and generate server's
     // Create a Request message with FQDN option and generate server's
     // response using processRequest function. This will result in the
     // response using processRequest function. This will result in the
     // creation of a new lease and the appropriate NameChangeRequest
     // creation of a new lease and the appropriate NameChangeRequest
     // to add both reverse and forward mapping to DNS.
     // to add both reverse and forward mapping to DNS.
     testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
     testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
-                       "myhost.example.com.", srv);
-    ASSERT_EQ(1, srv.name_change_reqs_.size());
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+                       "myhost.example.com.");
+    ASSERT_EQ(1, d2_mgr_.getQueueSize());
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
                             "2001:db8:1:1::dead:beef",
                             "2001:db8:1:1::dead:beef",
                             "000201415AA33D1187D148275136FA30300478"
                             "000201415AA33D1187D148275136FA30300478"
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
@@ -852,14 +820,14 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestRenew) {
     // should be added. Therefore, we expect two NameChangeRequests. One to
     // should be added. Therefore, we expect two NameChangeRequests. One to
     // remove the existing entries, one to add new entries.
     // remove the existing entries, one to add new entries.
     testProcessMessage(DHCPV6_RENEW, "otherhost.example.com",
     testProcessMessage(DHCPV6_RENEW, "otherhost.example.com",
-                       "otherhost.example.com.", srv);
-    ASSERT_EQ(2, srv.name_change_reqs_.size());
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
+                       "otherhost.example.com.");
+    ASSERT_EQ(2, d2_mgr_.getQueueSize());
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE, true, true,
                             "2001:db8:1:1::dead:beef",
                             "2001:db8:1:1::dead:beef",
                             "000201415AA33D1187D148275136FA30300478"
                             "000201415AA33D1187D148275136FA30300478"
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
                             0, 4000);
                             0, 4000);
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
                             "2001:db8:1:1::dead:beef",
                             "2001:db8:1:1::dead:beef",
                             "000201D422AA463306223D269B6CB7AFE7AAD265FC"
                             "000201D422AA463306223D269B6CB7AFE7AAD265FC"
                             "EA97F93623019B2E0D14E5323D5A",
                             "EA97F93623019B2E0D14E5323D5A",
@@ -868,16 +836,14 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestRenew) {
 }
 }
 
 
 TEST_F(FqdnDhcpv6SrvTest, processRequestRelease) {
 TEST_F(FqdnDhcpv6SrvTest, processRequestRelease) {
-    NakedDhcpv6Srv srv(0);
-
     // Create a Request message with FQDN option and generate server's
     // Create a Request message with FQDN option and generate server's
     // response using processRequest function. This will result in the
     // response using processRequest function. This will result in the
     // creation of a new lease and the appropriate NameChangeRequest
     // creation of a new lease and the appropriate NameChangeRequest
     // to add both reverse and forward mapping to DNS.
     // to add both reverse and forward mapping to DNS.
     testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
     testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
-                       "myhost.example.com.", srv);
-    ASSERT_EQ(1, srv.name_change_reqs_.size());
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+                       "myhost.example.com.");
+    ASSERT_EQ(1, d2_mgr_.getQueueSize());
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
                             "2001:db8:1:1::dead:beef",
                             "2001:db8:1:1::dead:beef",
                             "000201415AA33D1187D148275136FA30300478"
                             "000201415AA33D1187D148275136FA30300478"
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
@@ -888,9 +854,9 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestRelease) {
     // also removed. Therefore, we expect that single NameChangeRequest to
     // also removed. Therefore, we expect that single NameChangeRequest to
     // remove DNS entries is generated.
     // remove DNS entries is generated.
     testProcessMessage(DHCPV6_RELEASE, "otherhost.example.com",
     testProcessMessage(DHCPV6_RELEASE, "otherhost.example.com",
-                       "otherhost.example.com.", srv);
-    ASSERT_EQ(1, srv.name_change_reqs_.size());
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
+                       "otherhost.example.com.");
+    ASSERT_EQ(1, d2_mgr_.getQueueSize());
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE, true, true,
                             "2001:db8:1:1::dead:beef",
                             "2001:db8:1:1::dead:beef",
                             "000201415AA33D1187D148275136FA30300478"
                             "000201415AA33D1187D148275136FA30300478"
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
@@ -901,15 +867,13 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestRelease) {
 // Checks that the server include DHCPv6 Client FQDN option in its
 // Checks that the server include DHCPv6 Client FQDN option in its
 // response even when client doesn't request this option using ORO.
 // response even when client doesn't request this option using ORO.
 TEST_F(FqdnDhcpv6SrvTest, processRequestWithoutFqdn) {
 TEST_F(FqdnDhcpv6SrvTest, processRequestWithoutFqdn) {
-    NakedDhcpv6Srv srv(0);
-
     // The last parameter disables use of the ORO to request FQDN option
     // The last parameter disables use of the ORO to request FQDN option
     // In this case, we expect that the FQDN option will not be included
     // In this case, we expect that the FQDN option will not be included
     // in the server's response. The testProcessMessage will check that.
     // in the server's response. The testProcessMessage will check that.
     testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
     testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
-                       "myhost.example.com.", srv, false);
-    ASSERT_EQ(1, srv.name_change_reqs_.size());
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+                       "myhost.example.com.", false);
+    ASSERT_EQ(1, d2_mgr_.getQueueSize());
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
                             "2001:db8:1:1::dead:beef",
                             "2001:db8:1:1::dead:beef",
                             "000201415AA33D1187D148275136FA30300478"
                             "000201415AA33D1187D148275136FA30300478"
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
@@ -919,13 +883,10 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestWithoutFqdn) {
 // Checks that FQDN is generated from an ip address, when client sends an empty
 // Checks that FQDN is generated from an ip address, when client sends an empty
 // FQDN.
 // FQDN.
 TEST_F(FqdnDhcpv6SrvTest, processRequestEmptyFqdn) {
 TEST_F(FqdnDhcpv6SrvTest, processRequestEmptyFqdn) {
-    NakedDhcpv6Srv srv(0);
-
     testProcessMessage(DHCPV6_REQUEST, "",
     testProcessMessage(DHCPV6_REQUEST, "",
-                       "myhost-2001-db8-1-1--dead-beef.example.com.",
-                       srv, false);
-    ASSERT_EQ(1, srv.name_change_reqs_.size());
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+                       "myhost-2001-db8-1-1--dead-beef.example.com.", false);
+    ASSERT_EQ(1, d2_mgr_.getQueueSize());
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
                             "2001:db8:1:1::dead:beef",
                             "2001:db8:1:1::dead:beef",
                             "000201C905E54BE12DE6AF92ADE72752B9F362"
                             "000201C905E54BE12DE6AF92ADE72752B9F362"
                             "13B5A8BC9D217548CD739B4CF31AFB1B",
                             "13B5A8BC9D217548CD739B4CF31AFB1B",
@@ -951,12 +912,11 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestReuseExpiredLease) {
     CfgMgr::instance().addSubnet6(subnet_);
     CfgMgr::instance().addSubnet6(subnet_);
 
 
     // Allocate a lease.
     // Allocate a lease.
-    NakedDhcpv6Srv srv(0);
     testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
     testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
-                       "myhost.example.com.", srv);
+                       "myhost.example.com.");
     // Test that the appropriate NameChangeRequest has been generated.
     // Test that the appropriate NameChangeRequest has been generated.
-    ASSERT_EQ(1, srv.name_change_reqs_.size());
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+    ASSERT_EQ(1, d2_mgr_.getQueueSize());
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
                             "2001:db8:1:1::dead:beef",
                             "2001:db8:1:1::dead:beef",
                             "000201415AA33D1187D148275136FA30300478"
                             "000201415AA33D1187D148275136FA30300478"
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
@@ -986,18 +946,18 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestReuseExpiredLease) {
     // lease database, it is guaranteed that the allocation engine will
     // lease database, it is guaranteed that the allocation engine will
     // reuse this lease.
     // reuse this lease.
     testProcessMessage(DHCPV6_REQUEST, "myhost.example.com.",
     testProcessMessage(DHCPV6_REQUEST, "myhost.example.com.",
-                       "myhost.example.com.", srv);
-    ASSERT_EQ(2, srv.name_change_reqs_.size());
+                       "myhost.example.com.");
+    ASSERT_EQ(2, d2_mgr_.getQueueSize());
     // The first name change request generated, should remove a DNS
     // The first name change request generated, should remove a DNS
     // mapping for an expired lease.
     // mapping for an expired lease.
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE, true, true,
                             "2001:db8:1:1::dead:beef",
                             "2001:db8:1:1::dead:beef",
                             "000201D422AA463306223D269B6CB7AFE7AAD2"
                             "000201D422AA463306223D269B6CB7AFE7AAD2"
                             "65FCEA97F93623019B2E0D14E5323D5A",
                             "65FCEA97F93623019B2E0D14E5323D5A",
                             0, 5);
                             0, 5);
     // The second name change request should add a DNS mapping for
     // The second name change request should add a DNS mapping for
     // a new lease.
     // a new lease.
-    verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+    verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
                             "2001:db8:1:1::dead:beef",
                             "2001:db8:1:1::dead:beef",
                             "000201415AA33D1187D148275136FA30300478"
                             "000201415AA33D1187D148275136FA30300478"
                             "FAAAA3EBD29826B5C907B2C9268A6F52",
                             "FAAAA3EBD29826B5C907B2C9268A6F52",