BIND 10 trac3088, updated. dc1071d7aa1204e731c0a5959d28dc3425aa7f3b [3088] Initial implementation of d2::NameRemoveTransaction
BIND 10 source code commits
bind10-changes at lists.isc.org
Fri Dec 6 21:30:48 UTC 2013
The branch, trac3088 has been updated
via dc1071d7aa1204e731c0a5959d28dc3425aa7f3b (commit)
from 4895dc78cc15d7b89ab782d7e74ef2ab90255da3 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit dc1071d7aa1204e731c0a5959d28dc3425aa7f3b
Author: Thomas Markwalder <tmark at isc.org>
Date: Fri Dec 6 16:28:02 2013 -0500
[3088] Initial implementation of d2::NameRemoveTransaction
Interrim check-in for 3088, which introduces the initial
implementation of d2:NameRemoveTransaction. This class
implements the state model logic necessary for removing
DNS entries per RFC 4703. The state model logic is
complete, what remains is the logic to build the actual
request messages.
-----------------------------------------------------------------------
Summary of changes:
src/bin/d2/Makefile.am | 1 +
src/bin/d2/d2_messages.mes | 87 ++
src/bin/d2/nc_add.cc | 9 +-
src/bin/d2/nc_add.h | 13 +-
src/bin/d2/{nc_add.cc => nc_remove.cc} | 248 ++--
src/bin/d2/{nc_add.h => nc_remove.h} | 290 ++--
src/bin/d2/nc_trans.cc | 6 +-
src/bin/d2/nc_trans.h | 2 +-
src/bin/d2/tests/Makefile.am | 2 +
src/bin/d2/tests/nc_add_unittests.cc | 26 +-
...{nc_add_unittests.cc => nc_remove_unittests.cc} | 1487 ++++++++++----------
src/bin/d2/tests/nc_test_utils.cc | 159 +++
src/bin/d2/tests/nc_test_utils.h | 83 ++
13 files changed, 1383 insertions(+), 1030 deletions(-)
copy src/bin/d2/{nc_add.cc => nc_remove.cc} (74%)
copy src/bin/d2/{nc_add.h => nc_remove.h} (52%)
copy src/bin/d2/tests/{nc_add_unittests.cc => nc_remove_unittests.cc} (50%)
-----------------------------------------------------------------------
diff --git a/src/bin/d2/Makefile.am b/src/bin/d2/Makefile.am
index 43f3e39..1da489c 100644
--- a/src/bin/d2/Makefile.am
+++ b/src/bin/d2/Makefile.am
@@ -65,6 +65,7 @@ b10_dhcp_ddns_SOURCES += d2_zone.cc d2_zone.h
b10_dhcp_ddns_SOURCES += dns_client.cc dns_client.h
b10_dhcp_ddns_SOURCES += labeled_value.cc labeled_value.h
b10_dhcp_ddns_SOURCES += nc_add.cc nc_add.h
+b10_dhcp_ddns_SOURCES += nc_remove.cc nc_remove.h
b10_dhcp_ddns_SOURCES += nc_trans.cc nc_trans.h
b10_dhcp_ddns_SOURCES += state_model.cc state_model.h
diff --git a/src/bin/d2/d2_messages.mes b/src/bin/d2/d2_messages.mes
index 62ffd08..f5d7835 100644
--- a/src/bin/d2/d2_messages.mes
+++ b/src/bin/d2/d2_messages.mes
@@ -351,3 +351,90 @@ additions which were received and accepted by an appropriate DNS server.
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
documented in preceding log entries.
+
+% DHCP_DDNS_FORWARD_REMOVE_ADDRS_BUILD_FAILURE A DNS udpate message to remove a forward DNS Address entry could not be constructed for this request: %1 reason: %2
+This is an error message issued when an error occurs attempting to construct
+the server bound packet requesting a forward address (A or AAAA) removal. This
+is due to invalid data contained in the NameChangeRequest. The request will be
+aborted. This is most likely a configuration issue.
+
+% DHCP_DDNS_FORWARD_REMOVE_ADDRS_REJECTED DNS Server, %1, rejected a DNS update request to remove the forward address mapping for FQDN, %2, with an RCODE: %3
+This is an error message issued when an update was rejected by the DNS server
+it was sent to for the reason given by the RCODE. The rcode values are defined
+in RFC 2136.
+
+% DHCP_DDNS_FORWARD_REMOVE_ADDRS_IO_ERROR DHCP_DDNS encountered an IO error sending a forward mapping address removal for FQDN %1 to DNS server %2
+This is an error message issued when a communication error occurs while
+DHCP_DDNS is carrying out a forward address remove. The application will retry
+against the same server or others as appropriate.
+
+% DHCP_DDNS_FORWARD_REMOVE_ADDRS_RESP_CORRUPT DHCP_DDNS received a corrupt response from the DNS server, %1, while removing forward address mapping for FQDN, %2
+This is an error message issued when the response received by DHCP_DDNS, to a
+update request to remove a forward address mapping, is mangled or malformed.
+The application will retry against the same server or others as appropriate.
+
+% DHCP_DDNS_FORWARD_REMOVE_ADDRS_BAD_DNSCLIENT_STATUS DHCP_DDNS received an unknown DNSClient status: %1, while removing a forward address mapping for FQDN %2 to DNS server %3
+This is an error message issued when DNSClient returns an unrecognized status
+while DHCP_DDNS was removing a forward address mapping. The request will be
+aborted. This is most likely a programmatic issue and should be reported.
+
+% DHCP_DDNS_FORWARD_REMOVE_RRS_BUILD_FAILURE A DNS udpate message to remove forward DNS RR entries could not be constructed for this request: %1 reason: %2
+This is an error message issued when an error occurs attempting to construct
+the server bound packet requesting forward RR (DHCID RR) removal. This is due
+to invalid data contained in the NameChangeRequest. The request will be aborted.This is most likely a configuration issue.
+
+% DHCP_DDNS_FORWARD_REMOVE_RRS_REJECTED DNS Server, %1, rejected a DNS update request to remove forward RR entries for FQDN, %2, with an RCODE: %3
+This is an error message issued when an update was rejected by the DNS server
+it was sent to for the reason given by the RCODE. The rcode values are defined
+in RFC 2136.
+
+% DHCP_DDNS_FORWARD_REMOVE_RRS_IO_ERROR DHCP_DDNS encountered an IO error sending a forward RR removal for FQDN %1 to DNS server %2
+This is an error message issued when a communication error occurs while
+DHCP_DDNS is carrying out a forward RR remove. The application will retry
+against the same server.
+
+% DHCP_DDNS_FORWARD_REMOVE_RRS_RESP_CORRUPT DHCP_DDNS received a corrupt response from the DNS server, %1, while removing forward RRs for FQDN, %2
+This is an error message issued when the response received by DHCP_DDNS, to a
+update request to remove forward RRs mapping, is mangled or malformed.
+The application will retry against the same server or others as appropriate.
+
+% DHCP_DDNS_FORWARD_REMOVE_RRS_BAD_DNSCLIENT_STATUS DHCP_DDNS received an unknown DNSClient status: %1, while removing forward RRs for FQDN %2 to DNS server %3
+This is an error message issued when DNSClient returns an unrecognized status
+while DHCP_DDNS was removing forward RRs. The request will be aborted. This is
+most likely a programmatic issue and should be reported.
+
+% DHCP_DDNS_REVERSE_REMOVE_BUILD_FAILURE A DNS update message to remove a reverse DNS entry could not be constructed from this request: %1 reason: %2
+This is an error message issued when an error occurs attempting to construct
+the server bound packet requesting a reverse PTR removal. This is
+due to invalid data contained in the NameChangeRequest. The request will be
+aborted. This is most likely a configuration issue.
+
+% DHCP_DDNS_REVERSE_REMOVE_REJECTED DNS Server, %1, rejected a DNS update request to remove the reverse mapping for FQDN, %2, with an RCODE: %3
+This is an error message issued when an update was rejected by the DNS server
+it was sent to for the reason given by the RCODE. The rcode values are defined
+in RFC 2136.
+
+% DHCP_DDNS_REVERSE_REMOVE_IO_ERROR DHCP_DDNS encountered an IO error sending a reverse mapping remove for FQDN %1 to DNS server %2
+This is an error message issued when a communication error occurs while
+DHCP_DDNS is carrying out a reverse address update. The application will
+retry against the same server or others as appropriate.
+
+% DHCP_DDNS_REVERSE_REMOVE_RESP_CORRUPT DHCP_DDNS received a corrupt response from the DNS server, %1, while removing reverse address mapping for FQDN, %2
+This is an error message issued when the response received by DHCP_DDNS, to a
+update request to remove a reverse address, is mangled or malformed.
+The application will retry against the same server or others as appropriate.
+
+% DHCP_DDNS_REVERSE_REMOVE_BAD_DNSCLIENT_STATUS DHCP_DDNS received an unknown DNSClient status: %1, while removing reverse address mapping for FQDN %2 to DNS server %3
+This is an error message issued when DNSClient returns an unrecognized status
+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.
+
+% 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.
+
+% DHCP_DDNS_REMOVE_FAILED DHCP_DDNS failed attempting to make DNS mapping removals for this request: %1
+This is an error message issued after DHCP_DDNS attempts to submit DNS mapping
+entry removals have failed. There precise reason for the failure should be
+documented in preceding log entries.
+
diff --git a/src/bin/d2/nc_add.cc b/src/bin/d2/nc_add.cc
index b92ec56..e284940 100644
--- a/src/bin/d2/nc_add.cc
+++ b/src/bin/d2/nc_add.cc
@@ -54,7 +54,7 @@ NameAddTransaction::defineEvents() {
// Call superclass impl first.
NameChangeTransaction::defineEvents();
- // Define NCT events.
+ // Define NameAddTransaction events.
defineEvent(FQDN_IN_USE_EVT, "FQDN_IN_USE_EVT");
defineEvent(FQDN_NOT_IN_USE_EVT, "FQDN_NOT_IN_USE_EVT");
}
@@ -64,7 +64,7 @@ NameAddTransaction::verifyEvents() {
// Call superclass impl first.
NameChangeTransaction::verifyEvents();
- // Verify NCT events.
+ // Verify NameAddTransaction events.
getEvent(FQDN_IN_USE_EVT);
getEvent(FQDN_NOT_IN_USE_EVT);
}
@@ -74,7 +74,7 @@ NameAddTransaction::defineStates() {
// Call superclass impl first.
NameChangeTransaction::defineStates();
- // Define the states.
+ // Define NameAddTransaction states.
defineState(READY_ST, "READY_ST",
boost::bind(&NameAddTransaction::readyHandler, this));
@@ -105,7 +105,7 @@ NameAddTransaction::verifyStates() {
// Call superclass impl first.
NameChangeTransaction::verifyStates();
- // Verify NCT states. This ensures that derivations provide the handlers.
+ // Verify NameAddTransaction states.
getState(ADDING_FWD_ADDRS_ST);
getState(REPLACING_FWD_ADDRS_ST);
getState(REPLACING_REV_PTRS_ST);
@@ -541,6 +541,7 @@ void
NameAddTransaction::processAddFailedHandler() {
switch(getNextEvent()) {
case UPDATE_FAILED_EVT:
+ case NO_MORE_SERVERS_EVT:
LOG_ERROR(dctl_logger, DHCP_DDNS_ADD_FAILED).arg(getNcr()->toText());
setNcrStatus(dhcp_ddns::ST_FAILED);
endModel();
diff --git a/src/bin/d2/nc_add.h b/src/bin/d2/nc_add.h
index b5776b4..a9e748f 100644
--- a/src/bin/d2/nc_add.h
+++ b/src/bin/d2/nc_add.h
@@ -34,9 +34,10 @@ public:
/// @brief Embodies the "life-cycle" required to carry out a DDNS Add update.
///
/// NameAddTransaction implements a state machine for adding (or replacing) a
-/// forward DNS mapping. This state machine is based upon the processing logic
-/// described in RFC 4703, Sections 5.3 and 5.4. That logic may be paraphrased
-/// as follows:
+/// forward and/or reverse DNS mapping. This state machine is based upon the
+/// processing logic described in RFC 4703, Sections 5.3 and 5.4. That logic
+/// may be paraphrased as follows:
+///
/// @code
///
/// If the request includes a forward change:
@@ -166,7 +167,7 @@ protected:
/// handler simply attempts to select the next server.
///
/// Transitions to:
- /// - ADDING_REV_PTRS_ST with next event of SERVER_SELECTED upon successful
+ /// - ADDING_FWD_ADDRS_ST with next event of SERVER_SELECTED upon successful
/// server selection
///
/// - PROCESS_TRANS_FAILED with next event of NO_MORE_SERVERS_EVT upon
@@ -329,7 +330,7 @@ protected:
/// - PROCESS_TRANS_OK_ST with a next event of UPDATE_OK_EVT upon
/// successful replacement.
///
- /// - PROCESS_TRANS_FAILED_ST with a next event of UPDATE_OK_EVT If the
+ /// - PROCESS_TRANS_FAILED_ST with a next event of UPDATE_FAILED_EVT If the
/// DNS server rejected the update for any reason or the IO completed
/// with an unrecognized status.
///
@@ -365,8 +366,10 @@ protected:
/// @brief State handler for PROCESS_TRANS_FAILED_ST.
///
/// Entered from:
+ /// - SELECTING_FWD_SERVER_ST with a next event of NO_MORE_SERVERS
/// - ADDING_FWD_ADDRS_ST with a next event of UPDATE_FAILED_EVT
/// - REPLACING_FWD_ADDRS_ST with a next event of UPDATE_FAILED_EVT
+ /// - SELECTING_REV_SERVER_ST with a next event of NO_MORE_SERVERS
/// - REPLACING_REV_PTRS_ST with a next event of UPDATE_FAILED_EVT
///
/// Sets the transaction status to indicate failure and ends
diff --git a/src/bin/d2/nc_remove.cc b/src/bin/d2/nc_remove.cc
new file mode 100644
index 0000000..17898f0
--- /dev/null
+++ b/src/bin/d2/nc_remove.cc
@@ -0,0 +1,695 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <d2/d2_log.h>
+#include <d2/nc_remove.h>
+
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+
+namespace isc {
+namespace d2 {
+
+
+// NameRemoveTransaction states
+const int NameRemoveTransaction::REMOVING_FWD_ADDRS_ST;
+const int NameRemoveTransaction::REMOVING_FWD_RRS_ST;
+const int NameRemoveTransaction::REMOVING_REV_PTRS_ST;
+
+// NameRemoveTransaction events
+// @todo none so far
+
+NameRemoveTransaction::
+NameRemoveTransaction(IOServicePtr& io_service,
+ dhcp_ddns::NameChangeRequestPtr& ncr,
+ DdnsDomainPtr& forward_domain,
+ DdnsDomainPtr& reverse_domain)
+ : NameChangeTransaction(io_service, ncr, forward_domain, reverse_domain) {
+ if (ncr->getChangeType() != isc::dhcp_ddns::CHG_REMOVE) {
+ isc_throw (NameRemoveTransactionError,
+ "NameRemoveTransaction, request type must be CHG_REMOVE");
+ }
+}
+
+NameRemoveTransaction::~NameRemoveTransaction(){
+}
+
+void
+NameRemoveTransaction::defineEvents() {
+ // Call superclass impl first.
+ NameChangeTransaction::defineEvents();
+
+ // Define NameRemoveTransaction events.
+ // @todo none so far
+ // defineEvent(TBD_EVENT, "TBD_EVT");
+}
+
+void
+NameRemoveTransaction::verifyEvents() {
+ // Call superclass impl first.
+ NameChangeTransaction::verifyEvents();
+
+ // Verify NameRemoveTransaction events.
+ // @todo none so far
+ // getEvent(TBD_EVENT);
+}
+
+void
+NameRemoveTransaction::defineStates() {
+ // Call superclass impl first.
+ NameChangeTransaction::defineStates();
+
+ // Define NameRemoveTransaction states.
+ defineState(READY_ST, "READY_ST",
+ boost::bind(&NameRemoveTransaction::readyHandler, this));
+
+ defineState(SELECTING_FWD_SERVER_ST, "SELECTING_FWD_SERVER_ST",
+ boost::bind(&NameRemoveTransaction::selectingFwdServerHandler,
+ this));
+
+ defineState(SELECTING_REV_SERVER_ST, "SELECTING_REV_SERVER_ST",
+ boost::bind(&NameRemoveTransaction::selectingRevServerHandler,
+ this));
+
+ defineState(REMOVING_FWD_ADDRS_ST, "REMOVING_FWD_ADDRS_ST",
+ boost::bind(&NameRemoveTransaction::removingFwdAddrsHandler,
+ this));
+
+ defineState(REMOVING_FWD_RRS_ST, "REMOVING_FWD_RRS_ST",
+ boost::bind(&NameRemoveTransaction::removingFwdRRsHandler,
+ this));
+
+ defineState(REMOVING_REV_PTRS_ST, "REMOVING_REV_PTRS_ST",
+ boost::bind(&NameRemoveTransaction::removingRevPtrsHandler,
+ this));
+
+ defineState(PROCESS_TRANS_OK_ST, "PROCESS_TRANS_OK_ST",
+ boost::bind(&NameRemoveTransaction::processRemoveOkHandler,
+ this));
+
+ defineState(PROCESS_TRANS_FAILED_ST, "PROCESS_TRANS_FAILED_ST",
+ boost::bind(&NameRemoveTransaction::processRemoveFailedHandler,
+ this));
+}
+
+void
+NameRemoveTransaction::verifyStates() {
+ // Call superclass impl first.
+ NameChangeTransaction::verifyStates();
+
+ // Verify NameRemoveTransaction states.
+ getState(REMOVING_FWD_ADDRS_ST);
+ getState(REMOVING_FWD_RRS_ST);
+ getState(REMOVING_REV_PTRS_ST);
+}
+
+void
+NameRemoveTransaction::readyHandler() {
+ switch(getNextEvent()) {
+ case START_EVT:
+ if (getForwardDomain()) {
+ // Request includes a forward change, do that first.
+ transition(SELECTING_FWD_SERVER_ST, SELECT_SERVER_EVT);
+ } else {
+ // Reverse change only, transition accordingly.
+ transition(SELECTING_REV_SERVER_ST, SELECT_SERVER_EVT);
+ }
+
+ break;
+ default:
+ // Event is invalid.
+ isc_throw(NameRemoveTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+}
+
+void
+NameRemoveTransaction::selectingFwdServerHandler() {
+ switch(getNextEvent()) {
+ case SELECT_SERVER_EVT:
+ // First time through for this transaction, so initialize server
+ // selection.
+ initServerSelection(getForwardDomain());
+ break;
+ case SERVER_IO_ERROR_EVT:
+ // We failed to communicate with current server. Attempt to select
+ // another one below.
+ break;
+ default:
+ // Event is invalid.
+ isc_throw(NameRemoveTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+
+ // Select the next server from the list of forward servers.
+ if (selectNextServer()) {
+ // We have a server to try.
+ transition(REMOVING_FWD_ADDRS_ST, SERVER_SELECTED_EVT);
+ }
+ else {
+ // Server list is exhausted, so fail the transaction.
+ transition(PROCESS_TRANS_FAILED_ST, NO_MORE_SERVERS_EVT);
+ }
+}
+
+void
+NameRemoveTransaction::removingFwdAddrsHandler() {
+ if (doOnEntry()) {
+ // Clear the request on initial transition. This allows us to reuse
+ // the request on retries if necessary.
+ clearDnsUpdateRequest();
+ }
+
+ switch(getNextEvent()) {
+ case SERVER_SELECTED_EVT:
+ if (!getDnsUpdateRequest()) {
+ // Request hasn't been constructed yet, so build it.
+ try {
+ buildRemoveFwdAddressRequest();
+ } catch (const std::exception& ex) {
+ // While unlikely, the build might fail if we have invalid
+ // data. Should that be the case, we need to fail the
+ // transaction.
+ LOG_ERROR(dctl_logger,
+ DHCP_DDNS_FORWARD_REMOVE_ADDRS_BUILD_FAILURE)
+ .arg(getNcr()->toText())
+ .arg(ex.what());
+ transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+ break;
+ }
+ }
+
+ // Call sendUpdate() to initiate the async send. Note it also sets
+ // next event to NOP_EVT.
+ sendUpdate();
+ break;
+
+ case IO_COMPLETED_EVT: {
+ switch (getDnsUpdateStatus()) {
+ case DNSClient::SUCCESS: {
+ // We successfully received a response packet from the server.
+ const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode();
+ if ((rcode == dns::Rcode::NOERROR()) ||
+ (rcode == dns::Rcode::NXDOMAIN())) {
+ // We were able to remove it or it wasn't there, now we
+ // need to remove any other RRs for this FQDN.
+ transition(REMOVING_FWD_RRS_ST, UPDATE_OK_EVT);
+ } else {
+ // Per RFC4703 any other value means cease.
+ // If we get not authorized should we try the next server in
+ // the list? @todo This needs some discussion perhaps.
+ LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REMOVE_ADDRS_REJECTED)
+ .arg(getCurrentServer()->getIpAddress())
+ .arg(getNcr()->getFqdn())
+ .arg(rcode.getCode());
+ transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+ }
+
+ break;
+ }
+
+ case DNSClient::TIMEOUT:
+ case DNSClient::OTHER:
+ // We couldn't send to the current server, log it and set up
+ // to select the next server for a retry.
+ // @note For now we treat OTHER as an IO error like TIMEOUT. It
+ // is not entirely clear if this is accurate.
+ LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REMOVE_ADDRS_IO_ERROR)
+ .arg(getNcr()->getFqdn())
+ .arg(getCurrentServer()->getIpAddress());
+
+ retryTransition(SELECTING_FWD_SERVER_ST);
+ break;
+
+ case DNSClient::INVALID_RESPONSE:
+ // A response was received but was corrupt. Retry it like an IO
+ // error.
+ LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REMOVE_ADDRS_RESP_CORRUPT)
+ .arg(getCurrentServer()->getIpAddress())
+ .arg(getNcr()->getFqdn());
+
+ retryTransition(SELECTING_FWD_SERVER_ST);
+ break;
+
+ default:
+ // Any other value and we will fail this transaction, something
+ // bigger is wrong.
+ LOG_ERROR(dctl_logger,
+ DHCP_DDNS_FORWARD_REMOVE_ADDRS_BAD_DNSCLIENT_STATUS)
+ .arg(getDnsUpdateStatus())
+ .arg(getNcr()->getFqdn())
+ .arg(getCurrentServer()->getIpAddress());
+
+ transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+ break;
+ } // end switch on dns_status
+
+ break;
+ } // end case IO_COMPLETE_EVT
+
+ default:
+ // Event is invalid.
+ isc_throw(NameRemoveTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+}
+
+
+void
+NameRemoveTransaction::removingFwdRRsHandler() {
+ if (doOnEntry()) {
+ // Clear the request on initial transition. This allows us to reuse
+ // the request on retries if necessary.
+ clearDnsUpdateRequest();
+ }
+
+ switch(getNextEvent()) {
+ case UPDATE_OK_EVT:
+ case SERVER_SELECTED_EVT:
+ if (!getDnsUpdateRequest()) {
+ // Request hasn't been constructed yet, so build it.
+ try {
+ buildRemoveFwdRRsRequest();
+ } catch (const std::exception& ex) {
+ // While unlikely, the build might fail if we have invalid
+ // data. Should that be the case, we need to fail the
+ // transaction.
+ LOG_ERROR(dctl_logger,
+ DHCP_DDNS_FORWARD_REMOVE_RRS_BUILD_FAILURE)
+ .arg(getNcr()->toText())
+ .arg(ex.what());
+ transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+ break;
+ }
+ }
+
+ // Call sendUpdate() to initiate the async send. Note it also sets
+ // next event to NOP_EVT.
+ sendUpdate();
+ break;
+
+ case IO_COMPLETED_EVT: {
+ switch (getDnsUpdateStatus()) {
+ case DNSClient::SUCCESS: {
+ // We successfully received a response packet from the server.
+ const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode();
+ // @todo Not sure if NXDOMAIN is ok here, but I think so.
+ // A Rcode of NXDOMAIN would mean there are no RRs for the FQDN,
+ // which is fine. We were asked to delete them, they are not there
+ // so all is well.
+ if ((rcode == dns::Rcode::NOERROR()) ||
+ (rcode == dns::Rcode::NXDOMAIN())) {
+ // We were able to remove the forward mapping. Mark it as done.
+ setForwardChangeCompleted(true);
+
+ // If request calls for reverse update then do that next,
+ // otherwise we can process ok.
+ if (getReverseDomain()) {
+ transition(SELECTING_REV_SERVER_ST, SELECT_SERVER_EVT);
+ } else {
+ transition(PROCESS_TRANS_OK_ST, UPDATE_OK_EVT);
+ }
+ } else {
+ // Per RFC4703 any other value means cease.
+ // If we get not authorized should try the next server in
+ // the list? @todo This needs some discussion perhaps.
+ LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REMOVE_RRS_REJECTED)
+ .arg(getCurrentServer()->getIpAddress())
+ .arg(getNcr()->getFqdn())
+ .arg(rcode.getCode());
+ transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+ }
+
+ break;
+ }
+
+ case DNSClient::TIMEOUT:
+ case DNSClient::OTHER:
+ // We couldn't send to the current server, log it and set up
+ // to select the next server for a retry.
+ // @note For now we treat OTHER as an IO error like TIMEOUT. It
+ // is not entirely clear if this is accurate.
+ LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REMOVE_RRS_IO_ERROR)
+ .arg(getNcr()->getFqdn())
+ .arg(getCurrentServer()->getIpAddress());
+
+ // @note If we exhaust the IO retries for the current server
+ // due to IO failures, we will abort the remaining updates.
+ // The rational is that we are only in this state, if the remove
+ // of the forward address RR succeeded (removingFwdAddrsHandler)
+ // on the current server. Therefore we should not attempt another
+ // removal on a different server. This is perhaps a point
+ // for discussion.
+ // @todo Should we go ahead with the reverse remove?
+ retryTransition(PROCESS_TRANS_FAILED_ST);
+ break;
+
+ case DNSClient::INVALID_RESPONSE:
+ // A response was received but was corrupt. Retry it like an IO
+ // error.
+ LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REMOVE_RRS_RESP_CORRUPT)
+ .arg(getCurrentServer()->getIpAddress())
+ .arg(getNcr()->getFqdn());
+
+ // If we are out of retries on this server abandon the transaction.
+ // (Same logic as the case for TIMEOUT above).
+ retryTransition(PROCESS_TRANS_FAILED_ST);
+ break;
+
+ default:
+ // Any other value and we will fail this transaction, something
+ // bigger is wrong.
+ LOG_ERROR(dctl_logger,
+ DHCP_DDNS_FORWARD_REMOVE_RRS_BAD_DNSCLIENT_STATUS)
+ .arg(getDnsUpdateStatus())
+ .arg(getNcr()->getFqdn())
+ .arg(getCurrentServer()->getIpAddress());
+
+ transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+ break;
+ } // end switch on dns_status
+
+ break;
+ } // end case IO_COMPLETE_EVT
+
+ default:
+ // Event is invalid.
+ isc_throw(NameRemoveTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+}
+
+
+void
+NameRemoveTransaction::selectingRevServerHandler() {
+ switch(getNextEvent()) {
+ case SELECT_SERVER_EVT:
+ // First time through for this transaction, so initialize server
+ // selection.
+ initServerSelection(getReverseDomain());
+ break;
+ case SERVER_IO_ERROR_EVT:
+ // We failed to communicate with current server. Attempt to select
+ // another one below.
+ break;
+ default:
+ // Event is invalid.
+ isc_throw(NameRemoveTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+
+ // Select the next server from the list of forward servers.
+ if (selectNextServer()) {
+ // We have a server to try.
+ transition(REMOVING_REV_PTRS_ST, SERVER_SELECTED_EVT);
+ }
+ else {
+ // Server list is exhausted, so fail the transaction.
+ transition(PROCESS_TRANS_FAILED_ST, NO_MORE_SERVERS_EVT);
+ }
+}
+
+
+void
+NameRemoveTransaction::removingRevPtrsHandler() {
+ if (doOnEntry()) {
+ // Clear the request on initial transition. This allows us to reuse
+ // the request on retries if necessary.
+ clearDnsUpdateRequest();
+ }
+
+ switch(getNextEvent()) {
+ case SERVER_SELECTED_EVT:
+ if (!getDnsUpdateRequest()) {
+ // Request hasn't been constructed yet, so build it.
+ try {
+ buildRemoveRevPtrsRequest();
+ } catch (const std::exception& ex) {
+ // While unlikely, the build might fail if we have invalid
+ // data. Should that be the case, we need to fail the
+ // transaction.
+ LOG_ERROR(dctl_logger, DHCP_DDNS_REVERSE_REMOVE_BUILD_FAILURE)
+ .arg(getNcr()->toText())
+ .arg(ex.what());
+ transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+ break;
+ }
+ }
+
+ // Call sendUpdate() to initiate the async send. Note it also sets
+ // next event to NOP_EVT.
+ sendUpdate();
+ break;
+
+ case IO_COMPLETED_EVT: {
+ switch (getDnsUpdateStatus()) {
+ case DNSClient::SUCCESS: {
+ // We successfully received a response packet from the server.
+ const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode();
+ if ((rcode == dns::Rcode::NOERROR()) ||
+ (rcode == dns::Rcode::NXDOMAIN())) {
+ // We were able to update the reverse mapping. Mark it as done.
+ // @todo For now we are also treating NXDOMAIN as success.
+ setReverseChangeCompleted(true);
+ transition(PROCESS_TRANS_OK_ST, UPDATE_OK_EVT);
+ } else {
+ // Per RFC4703 any other value means cease.
+ // If we get not authorized should try the next server in
+ // the list? @todo This needs some discussion perhaps.
+ LOG_ERROR(dctl_logger, DHCP_DDNS_REVERSE_REMOVE_REJECTED)
+ .arg(getCurrentServer()->getIpAddress())
+ .arg(getNcr()->getFqdn())
+ .arg(rcode.getCode());
+ transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+ }
+
+ break;
+ }
+
+ case DNSClient::TIMEOUT:
+ case DNSClient::OTHER:
+ // We couldn't send to the current server, log it and set up
+ // to select the next server for a retry.
+ // @note For now we treat OTHER as an IO error like TIMEOUT. It
+ // is not entirely clear if this is accurate.
+ LOG_ERROR(dctl_logger, DHCP_DDNS_REVERSE_REMOVE_IO_ERROR)
+ .arg(getNcr()->getFqdn())
+ .arg(getCurrentServer()->getIpAddress());
+
+ // If we are out of retries on this server, we go back and start
+ // all over on a new server.
+ retryTransition(SELECTING_REV_SERVER_ST);
+ break;
+
+ case DNSClient::INVALID_RESPONSE:
+ // A response was received but was corrupt. Retry it like an IO
+ // error.
+ LOG_ERROR(dctl_logger, DHCP_DDNS_REVERSE_REMOVE_RESP_CORRUPT)
+ .arg(getCurrentServer()->getIpAddress())
+ .arg(getNcr()->getFqdn());
+
+ // If we are out of retries on this server, we go back and start
+ // all over on a new server.
+ retryTransition(SELECTING_REV_SERVER_ST);
+ break;
+
+ default:
+ // Any other value and we will fail this transaction, something
+ // bigger is wrong.
+ LOG_ERROR(dctl_logger,
+ DHCP_DDNS_REVERSE_REMOVE_BAD_DNSCLIENT_STATUS)
+ .arg(getDnsUpdateStatus())
+ .arg(getNcr()->getFqdn())
+ .arg(getCurrentServer()->getIpAddress());
+
+ transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+ break;
+ } // end switch on dns_status
+
+ break;
+ } // end case IO_COMPLETE_EVT
+
+ default:
+ // Event is invalid.
+ isc_throw(NameRemoveTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+}
+
+
+void
+NameRemoveTransaction::processRemoveOkHandler() {
+ switch(getNextEvent()) {
+ case UPDATE_OK_EVT:
+ LOG_DEBUG(dctl_logger, DBGLVL_TRACE_DETAIL, DHCP_DDNS_REMOVE_SUCCEEDED)
+ .arg(getNcr()->toText());
+ setNcrStatus(dhcp_ddns::ST_COMPLETED);
+ endModel();
+ break;
+ default:
+ // Event is invalid.
+ isc_throw(NameRemoveTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+}
+
+
+void
+NameRemoveTransaction::processRemoveFailedHandler() {
+ switch(getNextEvent()) {
+ case UPDATE_FAILED_EVT:
+ case NO_MORE_SERVERS_EVT:
+ case SERVER_IO_ERROR_EVT:
+ LOG_ERROR(dctl_logger, DHCP_DDNS_REMOVE_FAILED).arg(getNcr()->toText());
+ setNcrStatus(dhcp_ddns::ST_FAILED);
+ endModel();
+ break;
+ default:
+ // Event is invalid.
+ isc_throw(NameRemoveTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+}
+
+void
+NameRemoveTransaction::buildRemoveFwdAddressRequest() {
+ // Construct an empty request.
+ D2UpdateMessagePtr request = prepNewRequest(getForwardDomain());
+
+#if 0
+ // Construct dns::Name from NCR fqdn.
+ dns::Name fqdn(dns::Name(getNcr()->getFqdn()));
+
+ // First build the Prerequisite Section.
+
+ // Create 'FQDN Is Not In Use' prerequisite (RFC 2136, section 2.4.5)
+ // Add the RR to prerequisite section.
+ dns::RRsetPtr prereq(new dns::RRset(fqdn, dns::RRClass::NONE(),
+ dns::RRType::ANY(), dns::RRTTL(0)));
+ request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq);
+
+ // Next build the Update Section.
+
+ // Create the FQDN/IP 'add' RR (RFC 2136, section 2.5.1)
+ // Set the message RData to lease address.
+ // Add the RR to update section.
+ dns::RRsetPtr update(new dns::RRset(fqdn, dns::RRClass::IN(),
+ getAddressRRType(), dns::RRTTL(0)));
+
+ addLeaseAddressRdata(update);
+ request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+ // Now create the FQDN/DHCID 'add' RR per RFC 4701)
+ // Set the message RData to DHCID.
+ // Add the RR to update section.
+ update.reset(new dns::RRset(fqdn, dns::RRClass::IN(),
+ dns::RRType::DHCID(), dns::RRTTL(0)));
+ addDhcidRdata(update);
+ request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+#endif
+
+ // Set the transaction's update request to the new request.
+ setDnsUpdateRequest(request);
+}
+
+void
+NameRemoveTransaction::buildRemoveFwdRRsRequest() {
+ // Construct an empty request.
+ D2UpdateMessagePtr request = prepNewRequest(getForwardDomain());
+
+#if 0
+ // Construct dns::Name from NCR fqdn.
+ dns::Name fqdn(dns::Name(getNcr()->getFqdn()));
+
+ // First build the Prerequisite Section.
+
+ // Create an 'FQDN Is In Use' prerequisite (RFC 2136, section 2.4.4)
+ // Add it to the pre-requisite section.
+ dns::RRsetPtr prereq(new dns::RRset(fqdn, dns::RRClass::ANY(),
+ dns::RRType::ANY(), dns::RRTTL(0)));
+ request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq);
+
+ // Now create an DHCID matches prerequisite RR.
+ // Set the RR's RData to DHCID.
+ // Add it to the pre-requisite section.
+ prereq.reset(new dns::RRset(fqdn, dns::RRClass::IN(),
+ dns::RRType::DHCID(), dns::RRTTL(0)));
+ addDhcidRdata(prereq);
+ request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq);
+
+ // Next build the Update Section.
+
+ // Create the FQDN/IP 'delete' RR (RFC 2136, section 2.5.1)
+ // Set the message RData to lease address.
+ // Add the RR to update section.
+ dns::RRsetPtr update(new dns::RRset(fqdn, dns::RRClass::ANY(),
+ getAddressRRType(), dns::RRTTL(0)));
+ request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+
+ // Create the FQDN/IP 'add' RR (RFC 2136, section 2.5.1)
+ // Set the message RData to lease address.
+ // Add the RR to update section.
+ update.reset(new dns::RRset(fqdn, dns::RRClass::IN(),
+ getAddressRRType(), dns::RRTTL(0)));
+ addLeaseAddressRdata(update);
+ request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+#endif
+
+ // Set the transaction's update request to the new request.
+ setDnsUpdateRequest(request);
+}
+
+void
+NameRemoveTransaction::buildRemoveRevPtrsRequest() {
+ // Construct an empty request.
+ D2UpdateMessagePtr request = prepNewRequest(getReverseDomain());
+
+#if 0
+ // Create the reverse IP address "FQDN".
+ std::string rev_addr = D2CfgMgr::reverseIpAddress(getNcr()->getIpAddress());
+ dns::Name rev_ip(rev_addr);
+
+ // Reverse replacement has no prerequisites so straight on to
+ // building the Update section.
+
+ // Create the PTR 'delete' RR and add it to update section.
+ dns::RRsetPtr update(new dns::RRset(rev_ip, dns::RRClass::ANY(),
+ dns::RRType::PTR(), dns::RRTTL(0)));
+ request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+
+ // Create the DHCID 'delete' RR and add it to the update section.
+ update.reset(new dns::RRset(rev_ip, dns::RRClass::ANY(),
+ dns::RRType::DHCID(), dns::RRTTL(0)));
+ request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+
+ // Create the FQDN/IP PTR 'add' RR, add the FQDN as the PTR Rdata
+ // then add it to update section.
+ update.reset(new dns::RRset(rev_ip, dns::RRClass::IN(),
+ dns::RRType::PTR(), dns::RRTTL(0)));
+ addPtrRdata(update);
+ request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+
+ // Create the FQDN/IP PTR 'add' RR, add the DHCID Rdata
+ // then add it to update section.
+ update.reset(new dns::RRset(rev_ip, dns::RRClass::IN(),
+ dns::RRType::DHCID(), dns::RRTTL(0)));
+ addDhcidRdata(update);
+ request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+#endif
+
+ // Set the transaction's update request to the new request.
+ setDnsUpdateRequest(request);
+}
+
+} // namespace isc::d2
+} // namespace isc
diff --git a/src/bin/d2/nc_remove.h b/src/bin/d2/nc_remove.h
new file mode 100644
index 0000000..d0a04ec
--- /dev/null
+++ b/src/bin/d2/nc_remove.h
@@ -0,0 +1,400 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef NC_REMOVE_H
+#define NC_REMOVE_H
+
+/// @file nc_remove.h This file defines the class NameRemoveTransaction.
+
+#include <d2/nc_trans.h>
+
+namespace isc {
+namespace d2 {
+
+/// @brief Thrown if the NameRemoveTransaction encounters a general error.
+class NameRemoveTransactionError : public isc::Exception {
+public:
+ NameRemoveTransactionError(const char* file, size_t line,
+ const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+/// @brief Embodies the "life-cycle" required to carry out a DDNS Remove update.
+///
+/// NameRemoveTransaction implements a state machine for removing a forward
+/// and/or reverse DNS mappings. This state machine is based upon the processing
+/// logic described in RFC 4703, Section 5.5. That logic may be paraphrased as
+/// follows:
+///
+/// @code
+///
+/// If the request includes a forward change:
+/// Select a forward server
+/// Send the server a request to remove client's specific forward address RR
+/// If it succeeds or the server responds with name no longer in use
+/// Send a server a request to delete any other RRs for that FQDN, such
+/// as the DHCID RR.
+/// otherwise
+/// abandon the update
+///
+/// If the request includes a reverse change:
+/// Select a reverse server
+/// Send a server a request to delete reverse entry (PTR RR)
+///
+/// @endcode
+///
+/// This class derives from NameChangeTransaction from which it inherits
+/// states, events, and methods common to NameChangeRequest processing.
+class NameRemoveTransaction : public NameChangeTransaction {
+public:
+
+ //@{ Additional states needed for NameRemove state model.
+ /// @brief State that attempts to remove specific forward address record.
+ static const int REMOVING_FWD_ADDRS_ST = NCT_DERIVED_STATE_MIN + 1;
+
+ /// @brief State that attempts to remove any other forward RRs for the DHCID
+ static const int REMOVING_FWD_RRS_ST = NCT_DERIVED_STATE_MIN + 2;
+
+ /// @brief State that attempts to remove reverse PTR records
+ static const int REMOVING_REV_PTRS_ST = NCT_DERIVED_STATE_MIN + 3;
+ //@}
+
+ //@{ Additional events needed for NameRemove state model.
+ /// @brief Event sent when replace attempt to fails with address not in use.
+ /// @todo Currently none have been identified.
+ //@}
+
+ /// @brief Constructor
+ ///
+ /// Instantiates an Remove transaction that is ready to be started.
+ ///
+ /// @param io_service IO service to be used for IO processing
+ /// @param ncr is the NameChangeRequest to fulfill
+ /// @param forward_domain is the domain to use for forward DNS updates
+ /// @param reverse_domain is the domain to use for reverse DNS updates
+ ///
+ /// @throw NameRemoveTransaction error if given request is not a CHG_REMOVE,
+ /// NameChangeTransaction error for base class construction errors.
+ NameRemoveTransaction(IOServicePtr& io_service,
+ dhcp_ddns::NameChangeRequestPtr& ncr,
+ DdnsDomainPtr& forward_domain,
+ DdnsDomainPtr& reverse_domain);
+
+ /// @brief Destructor
+ virtual ~NameRemoveTransaction();
+
+protected:
+ /// @brief Removes events defined by NameRemoveTransaction to the event set.
+ ///
+ /// Invokes NameChangeTransaction's implementation and then defines the
+ /// events unique to NCR Remove transaction processing.
+ ///
+ /// @throw StateModelError if an event definition is invalid or a duplicate.
+ virtual void defineEvents();
+
+ /// @brief Validates the contents of the set of events.
+ ///
+ /// Invokes NameChangeTransaction's implementation and then verifies the
+ /// Remove transaction's events.
+ ///
+ /// @throw StateModelError if an event value is undefined.
+ virtual void verifyEvents();
+
+ /// @brief Removes states defined by NameRemoveTransaction to the state set.
+ ///
+ /// Invokes NameChangeTransaction's implementation and then defines the
+ /// states unique to NCR Remove transaction processing.
+ ///
+ /// @throw StateModelError if an state definition is invalid or a duplicate.
+ virtual void defineStates();
+
+ /// @brief Validates the contents of the set of states.
+ ///
+ /// Invokes NameChangeTransaction's implementation and then verifies the
+ /// Remove transaction's states.
+ ///
+ /// @throw StateModelError if an event value is undefined.
+ virtual void verifyStates();
+
+ /// @brief State handler for READY_ST.
+ ///
+ /// Entered from:
+ /// - INIT_ST with next event of START_EVT
+ ///
+ /// The READY_ST is the state the model transitions into when the inherited
+ /// method, startTransaction() is invoked. This handler, therefore, is the
+ /// entry point into the state model execution.h Its primary task is to
+ /// determine whether to start with a forward DNS change or a reverse DNS
+ /// change.
+ ///
+ /// Transitions to:
+ /// - SELECTING_FWD_SERVER_ST with next event of SERVER_SELECT_ST if request
+ /// includes a forward change.
+ ///
+ /// - SELECTING_REV_SERVER_ST with next event of SERVER_SELECT_ST if request
+ /// includes only a reverse change.
+ ///
+ /// @throw NameRemoveTransactionError if upon entry next event is not
+ /// START_EVT.
+ void readyHandler();
+
+ /// @brief State handler for SELECTING_FWD_SERVER_ST.
+ ///
+ /// Entered from:
+ /// - READY_ST with next event of SELECT_SERVER_EVT
+ /// - REMOVING_FWD_ADDRS_ST with next event of SERVER_IO_ERROR_EVT
+ ///
+ /// Selects the server to be used from the forward domain for the forward
+ /// DNS update. If next event is SELECT_SERVER_EVT the handler initializes
+ /// the forward domain's server selection mechanism and then attempts to
+ /// select the next server. If next event is SERVER_IO_ERROR_EVT then the
+ /// handler simply attempts to select the next server.
+ ///
+ /// Transitions to:
+ /// - REMOVING_FWD_ADDRS_ST with next event of SERVER_SELECTED upon
+ /// successful server selection
+ ///
+ /// - PROCESS_TRANS_FAILED with next event of NO_MORE_SERVERS_EVT upon
+ /// failure to select a server
+ ///
+ /// @throw NameRemoveTransactionError if upon entry next event is not
+ /// SELECT_SERVER_EVT or SERVER_IO_ERROR_EVT.
+ void selectingFwdServerHandler();
+
+ /// @brief State handler for SELECTING_REV_SERVER_ST.
+ ///
+ /// Entered from:
+ /// - READY_ST with next event of SELECT_SERVER_EVT
+ /// - REMOVING_FWD_RRS_ST with next event of SELECT_SERVER_EVT
+ /// - REMOVING_REV_PTRS_ST with next event of SERVER_IO_ERROR_EVT
+ ///
+ /// Selects the server to be used from the reverse domain for the reverse
+ /// DNS update. If next event is SELECT_SERVER_EVT the handler initializes
+ /// the reverse domain's server selection mechanism and then attempts to
+ /// select the next server. If next event is SERVER_IO_ERROR_EVT then the
+ /// handler simply attempts to select the next server.
+ ///
+ /// Transitions to:
+ /// - REMOVING_REV_PTRS_ST with next event of SERVER_SELECTED upon
+ /// successful server selection
+ ///
+ /// - PROCESS_TRANS_FAILED with next event of NO_MORE_SERVERS_EVT upon
+ /// failure to select a server
+ ///
+ /// @throw NameRemoveTransactionError if upon entry next event is not
+ /// SELECT_SERVER_EVT or SERVER_IO_ERROR_EVT.
+ void selectingRevServerHandler();
+
+ /// @brief State handler for REMOVING_FWD_ADDRS_ST.
+ ///
+ /// Entered from:
+ /// - SELECTING_FWD_SERVER with next event of SERVER_SELECTED_EVT
+ ///
+ /// Attempts to remove the forward DNS entry for a given FQDN, provided
+ /// a DHCID RR exists which matches the requesting DHCID. If this is
+ /// first invocation of the handler after transitioning into this state,
+ /// any previous update request context is deleted. If next event
+ /// is SERVER_SELECTED_EVT, the handler builds the forward remove request,
+ /// schedules an asynchronous send via sendUpdate(), and returns. Note
+ /// that sendUpdate will post NOP_EVT as next event.
+ ///
+ /// Posting the NOP_EVT will cause runModel() to suspend execution of
+ /// the state model thus affecting a "wait" for the update IO to complete.
+ /// Update completion occurs via the DNSClient callback operator() method
+ /// inherited from NameChangeTransaction. When invoked this callback will
+ /// post a next event of IO_COMPLETED_EVT and then invoke runModel which
+ /// resumes execution of the state model.
+ ///
+ /// When the handler is invoked with a next event of IO_COMPELTED_EVT,
+ /// the DNS update status is checked and acted upon accordingly:
+ ///
+ /// Transitions to:
+ /// - REMOVING_FWD_RRS_ST with next event of UPDATE_OK_EVT upon successful
+ /// removal or RCODE of indication FQDN is no longer in use (NXDOMAIN).
+ ///
+ /// - PROCESS_TRANS_FAILED_ST with next event of UPDATE_FAILED_EVT if the
+ /// DNS server rejected the update for any reason or the IO completed
+ /// with an unrecognized status.
+ ///
+ /// - RE-ENTER this state with next event of SERVER_SELECTED_EVT if
+ /// there was an IO error communicating with the server and the number of
+ /// per server retries has not been exhausted.
+ ///
+ /// - SELECTING_FWD_SERVER_ST with next event of SERVER_IO_ERROR_EVT if
+ /// there was an IO error communicating with the server and the number of
+ /// per server retries has been exhausted.
+ ///
+ /// @throw NameRemoveTransactionError if upon entry next event is not
+ /// SERVER_SELECTED_EVT or IO_COMPLETE_EVT
+ void removingFwdAddrsHandler();
+
+ /// @brief State handler for REMOVING_FWD_RRS_ST.
+ ///
+ /// Entered from:
+ /// - REMOVING_FWD_ADDRS_ST with next event of UPDATE_OK_EVT
+ ///
+ /// Attempts to delete any remaining RRs associated with the given FQDN
+ /// such as the DHCID RR. If this is first invocation of the handler after
+ /// transitioning into this state, any previous update request context is
+ /// deleted and the handler builds the forward remove request. It then
+ /// schedules an asynchronous send via sendUpdate(),
+ /// and returns. Note that sendUpdate will post NOP_EVT as the next event.
+ ///
+ /// Posting the NOP_EVT will cause runModel() to suspend execution of
+ /// the state model thus affecting a "wait" for the update IO to complete.
+ /// Update completion occurs via the DNSClient callback operator() method
+ /// inherited from NameChangeTransaction. When invoked this callback will
+ /// post a next event of IO_COMPLETED_EVT and then invoke runModel which
+ /// resumes execution of the state model.
+ ///
+ /// When the handler is invoked with a next event of IO_COMPELTED_EVT,
+ /// the DNS update status is checked and acted upon accordingly:
+ ///
+ /// Transitions to:
+ /// - SELECTING_REV_SERVER_ST with a next event of SELECT_SERVER_EVT upon
+ /// successful completion and the request includes a reverse DNS update.
+ ///
+ /// - PROCESS_TRANS_OK_ST with next event of UPDATE_OK_EVT upon successful
+ /// completion and the request does not include a reverse DNS update.
+ ///
+ /// - PROCESS_TRANS_FAILED_ST with a next event of UPDATE_FAILED_EVT if the
+ /// DNS server rejected the update for any other reason or the IO completed
+ /// with an unrecognized status.
+ ///
+ /// - RE-ENTER this state with a next event of SERVER_SELECTED_EVT if
+ /// there was an IO error communicating with the server and the number of
+ /// per server retries has not been exhausted.
+ ///
+ /// - PROCESS_TRANS_FAILED_ST with a next event of SERVER_IO_ERROR_EVT if
+ /// there we have reached maximum number of retries without success on the
+ /// current server.
+ ///
+ /// @note If we exhaust the IO retries for the current server due to IO
+ /// failures, we will abort the remaining updates. The rational is that
+ /// we are only in this state, if the remove of the forward address RR
+ /// succeeded (removingFwdAddrsHandler) on the current server so we should
+ /// not attempt another removal on a different server. This is perhaps a
+ /// point for discussion. @todo Should we go ahead with the reverse remove?
+ ///
+ /// @throw NameRemoveTransactionError if upon entry next event is not:
+ /// UPDATE_OK_EVT or IO_COMPLETE_EVT
+ void removingFwdRRsHandler();
+
+ /// @brief State handler for REMOVING_REV_PTRS_ST.
+ ///
+ /// Entered from:
+ /// - SELECTING_REV_SERVER_ST with a next event of SERVER_SELECTED_EVT
+ ///
+ /// Attempts to delete a reverse DNS entry for a given FQDN. If this is
+ /// first invocation of the handler after transitioning into this state,
+ /// any previous update request context is deleted. If next event is
+ /// SERVER_SELECTED_EVT, the handler builds the reverse remove request,
+ /// schedules an asynchronous send via sendUpdate(), and then returns.
+ /// Note that sendUpdate will post NOP_EVT as next event.
+ ///
+ /// Posting the NOP_EVT will cause runModel() to suspend execution of
+ /// the state model thus affecting a "wait" for the update IO to complete.
+ /// Update completion occurs via the DNSClient callback operator() method
+ /// inherited from NameChangeTransaction. When invoked this callback will
+ /// post a next event of IO_COMPLETED_EVT and then invoke runModel which
+ /// resumes execution of the state model.
+ ///
+ /// When the handler is invoked with a next event of IO_COMPELTED_EVT,
+ /// the DNS update status is checked and acted upon accordingly:
+ ///
+ /// Transitions to:
+ /// - PROCESS_TRANS_OK_ST with a next event of UPDATE_OK_EVT upon
+ /// successful completion.
+ ///
+ /// - PROCESS_TRANS_FAILED_ST with a next event of UPDATE_FAILED_EVT If the
+ /// DNS server rejected the update for any reason or the IO completed
+ /// with an unrecognized status.
+ ///
+ /// - RE-ENTER this state with a next event of SERVER_SELECTED_EVT if
+ /// there was an IO error communicating with the server and the number of
+ /// per server retries has not been exhausted.
+ ///
+ /// - SELECTING_REV_SERVER_ST with next event of SERVER_IO_ERROR_EVT if
+ /// there was an IO error communicating with the server and the number of
+ /// per server retries has been exhausted.
+ ///
+ /// @throw NameRemoveTransactionError if upon entry next event is not:
+ /// SERVER_SELECTED_EVT or IO_COMPLETED_EVT
+ void removingRevPtrsHandler();
+
+ /// @brief State handler for PROCESS_TRANS_OK_ST.
+ ///
+ /// Entered from:
+ /// - REMOVING_FWD_RRS_ST with a next event of UPDATE_OK_EVT
+ /// - REMOVING_REV_PTRS_ST with a next event of UPDATE_OK_EVT
+ ///
+ /// Sets the transaction action status to indicate success and ends
+ /// model execution.
+ ///
+ /// Transitions to:
+ /// - END_ST with a next event of END_EVT.
+ ///
+ /// @throw NameRemoveTransactionError if upon entry next event is not:
+ /// UPDATE_OK_EVT
+ void processRemoveOkHandler();
+
+ /// @brief State handler for PROCESS_TRANS_FAILED_ST.
+ ///
+ /// Entered from:
+ /// - SELECTING_FWD_SERVER_ST with a next event of NO_MORE_SERVERS
+ /// - REMOVING_FWD_ADDRS_ST with a next event of UPDATE_FAILED_EVT
+ /// - REMOVING_FWD_RRS_ST with a next event of UPDATE_FAILED_EVT
+ /// - REMOVING_FWD_RRS_ST with a next event of SERVER_IO_ERROR_EVT
+ /// - SELECTING_REV_SERVER_ST with a next event of NO_MORE_SERVERS
+ /// - REMOVING_REV_PTRS_ST with a next event of UPDATE_FAILED_EVT
+ ///
+ /// Sets the transaction status to indicate failure and ends
+ /// model execution.
+ ///
+ /// Transitions to:
+ /// - END_ST with a next event of FAIL_EVT.
+ ///
+ /// @throw NameRemoveTransactionError if upon entry next event is not:
+ /// UPDATE_FAILED_EVT
+ void processRemoveFailedHandler();
+
+ /// @brief Builds a DNS request to add an forward DNS entry for an FQDN
+ ///
+ /// @todo - Method not implemented yet
+ ///
+ /// @throw isc::NotImplemented
+ void buildRemoveFwdAddressRequest();
+
+ /// @brief Builds a DNS request to replace forward DNS entry for an FQDN
+ ///
+ /// @todo - Method not implemented yet
+ ///
+ /// @throw isc::NotImplemented
+ void buildRemoveFwdRRsRequest();
+
+ /// @brief Builds a DNS request to replace a reverse DNS entry for an FQDN
+ ///
+ /// @todo - Method not implemented yet
+ ///
+ /// @throw isc::NotImplemented
+ void buildRemoveRevPtrsRequest();
+};
+
+/// @brief Defines a pointer to a NameChangeTransaction.
+typedef boost::shared_ptr<NameRemoveTransaction> NameRemoveTransactionPtr;
+
+
+} // namespace isc::d2
+} // namespace isc
+#endif
diff --git a/src/bin/d2/nc_trans.cc b/src/bin/d2/nc_trans.cc
index 3c604d2..b300f21 100644
--- a/src/bin/d2/nc_trans.cc
+++ b/src/bin/d2/nc_trans.cc
@@ -181,14 +181,14 @@ NameChangeTransaction::onModelFailure(const std::string& explanation) {
}
void
-NameChangeTransaction::retryTransition(const int server_sel_state) {
+NameChangeTransaction::retryTransition(const int fail_to_state) {
if (update_attempts_ < MAX_UPDATE_TRIES_PER_SERVER) {
// Re-enter the current state with same server selected.
transition(getCurrState(), SERVER_SELECTED_EVT);
} else {
- // Transition to given server selection state if we are out
+ // Transition to given fail_to_state state if we are out
// of retries.
- transition(server_sel_state, SERVER_IO_ERROR_EVT);
+ transition(fail_to_state, SERVER_IO_ERROR_EVT);
}
}
diff --git a/src/bin/d2/nc_trans.h b/src/bin/d2/nc_trans.h
index b0f7c4a..7415d88 100644
--- a/src/bin/d2/nc_trans.h
+++ b/src/bin/d2/nc_trans.h
@@ -280,7 +280,7 @@ protected:
/// @param server_sel_state State to transition to if maximum attempts
/// have been tried.
///
- void retryTransition(const int server_sel_state);
+ void retryTransition(const int fail_to_state);
/// @brief Sets the update request packet to the given packet.
///
diff --git a/src/bin/d2/tests/Makefile.am b/src/bin/d2/tests/Makefile.am
index d860ccc..8b5e351 100644
--- a/src/bin/d2/tests/Makefile.am
+++ b/src/bin/d2/tests/Makefile.am
@@ -67,6 +67,7 @@ d2_unittests_SOURCES += ../d2_zone.cc ../d2_zone.h
d2_unittests_SOURCES += ../dns_client.cc ../dns_client.h
d2_unittests_SOURCES += ../labeled_value.cc ../labeled_value.h
d2_unittests_SOURCES += ../nc_add.cc ../nc_add.h
+d2_unittests_SOURCES += ../nc_remove.cc ../nc_remove.h
d2_unittests_SOURCES += ../nc_trans.cc ../nc_trans.h
d2_unittests_SOURCES += ../state_model.cc ../state_model.h
d2_unittests_SOURCES += d_test_stubs.cc d_test_stubs.h
@@ -83,6 +84,7 @@ d2_unittests_SOURCES += d2_zone_unittests.cc
d2_unittests_SOURCES += dns_client_unittests.cc
d2_unittests_SOURCES += labeled_value_unittests.cc
d2_unittests_SOURCES += nc_add_unittests.cc
+d2_unittests_SOURCES += nc_remove_unittests.cc
d2_unittests_SOURCES += nc_test_utils.cc nc_test_utils.h
d2_unittests_SOURCES += nc_trans_unittests.cc
d2_unittests_SOURCES += state_model_unittests.cc
diff --git a/src/bin/d2/tests/nc_add_unittests.cc b/src/bin/d2/tests/nc_add_unittests.cc
index c291a54..4d2ad68 100644
--- a/src/bin/d2/tests/nc_add_unittests.cc
+++ b/src/bin/d2/tests/nc_add_unittests.cc
@@ -1419,8 +1419,7 @@ TEST_F(NameAddTransactionTest, replacingRevPtrsHandler_OtherRcode) {
name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED());
// Run replacingRevPtrsHandler again to process the response.
- //EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
- (name_add->replacingRevPtrsHandler());
+ EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
// Completion flags should still be false.
EXPECT_FALSE(name_add->getForwardChangeCompleted());
@@ -1634,6 +1633,29 @@ TEST_F(NameAddTransactionTest, processAddFailedHandler) {
EXPECT_THROW(name_add->processAddFailedHandler(), NameAddTransactionError);
}
+// Tests the processAddFailedHandler functionality.
+// It verifies behavior for posted event of NO_MORE_SERVERS_EVT.
+TEST_F(NameAddTransactionTest, processAddFailedHandler_NoMoreServers) {
+ NameAddStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::
+ PROCESS_TRANS_FAILED_ST,
+ NameChangeTransaction::
+ NO_MORE_SERVERS_EVT));
+
+ // Run processAddFailedHandler.
+ EXPECT_NO_THROW(name_remove->processAddFailedHandler());
+
+ // Verify that a server was selected.
+ EXPECT_EQ(dhcp_ddns::ST_FAILED, name_remove->getNcrStatus());
+
+ // Verify that the model has ended. (Remember, the transaction failed NOT
+ // the model. The model should have ended normally.)
+ EXPECT_EQ(StateModel::END_ST, name_remove->getCurrState());
+ EXPECT_EQ(StateModel::END_EVT, name_remove->getNextEvent());
+}
+
// Tests addingFwdAddrsHandler with the following scenario:
//
// The request includes only a forward change.
diff --git a/src/bin/d2/tests/nc_remove_unittests.cc b/src/bin/d2/tests/nc_remove_unittests.cc
new file mode 100644
index 0000000..02495e1
--- /dev/null
+++ b/src/bin/d2/tests/nc_remove_unittests.cc
@@ -0,0 +1,1874 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <d2/d2_cfg_mgr.h>
+#include <d2/d2_cfg_mgr.h>
+#include <d2/nc_remove.h>
+#include <dns/messagerenderer.h>
+#include <nc_test_utils.h>
+
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::d2;
+
+namespace {
+
+/// @brief Test class derived from NameRemoveTransaction to provide visiblity
+// to protected methods.
+class NameRemoveStub : public NameRemoveTransaction {
+public:
+ NameRemoveStub(IOServicePtr& io_service,
+ dhcp_ddns::NameChangeRequestPtr& ncr,
+ DdnsDomainPtr& forward_domain,
+ DdnsDomainPtr& reverse_domain)
+ : NameRemoveTransaction(io_service, ncr, forward_domain,
+ reverse_domain),
+ simulate_send_exception_(false),
+ simulate_build_request_exception_(false) {
+ }
+
+ virtual ~NameRemoveStub() {
+ }
+
+ /// @brief Simulates sending update requests to the DNS server
+ ///
+ /// This method simulates the initiation of an asynchronous send of
+ /// a DNS update request. It overrides the actual sendUpdate method in
+ /// the base class, thus avoiding an actual send, yet still increments
+ /// the update attempt count and posts a next event of NOP_EVT.
+ ///
+ /// It will also simulate an exception-based failure of sendUpdate, if
+ /// the simulate_send_exception_ flag is true.
+ ///
+ /// @param use_tsig_ Parameter is unused, but present in the base class
+ /// method.
+ ///
+ virtual void sendUpdate(bool /* use_tsig_ = false */) {
+ if (simulate_send_exception_) {
+ // Make the flag a one-shot by resetting it.
+ simulate_send_exception_ = false;
+ // Transition to failed.
+ transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+ return;
+ }
+
+ // Update send attempt count and post a NOP_EVT.
+ setUpdateAttempts(getUpdateAttempts() + 1);
+ postNextEvent(StateModel::NOP_EVT);
+ }
+
+ /// @brief Prepares the initial D2UpdateMessage
+ ///
+ /// This method overrides the NameChangeTransactio implementation to
+ /// provide the ability to simulate an exception throw in the build
+ /// request logic.
+ /// If the one-shot flag, simulate_build_request_exception_ is true,
+ /// this method will throw an exception, otherwise it will invoke the
+ /// base class method, providing normal functionality.
+ ///
+ /// For parameter description see the NameChangeTransaction implementation.
+ virtual D2UpdateMessagePtr prepNewRequest(DdnsDomainPtr domain) {
+ if (simulate_build_request_exception_) {
+ simulate_build_request_exception_ = false;
+ isc_throw (NameRemoveTransactionError,
+ "Simulated build requests exception");
+ }
+
+ return (NameChangeTransaction::prepNewRequest(domain));
+ }
+
+ /// @brief Simulates receiving a response
+ ///
+ /// This method simulates the completion of a DNSClient send. This allows
+ /// the state handler logic devoted to dealing with IO completion to be
+ /// fully exercise without requiring any actual IO. The two primary
+ /// pieces of information gleaned from IO completion are the DNSClient
+ /// status which indicates whether or not the IO exchange was successful
+ /// and the rcode, which indicates the server's reaction to the request.
+ ///
+ /// This method updates the transaction's DNS status value to that of the
+ /// given parameter, and then constructs and DNS update response message
+ /// with the given rcode value. To complete the simulation it then posts
+ /// a next event of IO_COMPLETED_EVT.
+ ///
+ /// @param status simulated DNSClient status
+ /// @param rcode simulated server response code
+ void fakeResponse(const DNSClient::Status& status,
+ const dns::Rcode& rcode) {
+ // Set the DNS update status. This is normally set in
+ // DNSClient IO completion handler.
+ setDnsUpdateStatus(status);
+
+ // Construct an empty message with the given Rcode.
+ D2UpdateMessagePtr msg(new D2UpdateMessage(D2UpdateMessage::OUTBOUND));
+ msg->setRcode(rcode);
+
+ // Set the update response to the message.
+ setDnsUpdateResponse(msg);
+
+ // Post the IO completion event.
+ postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+ }
+
+ /// @brief Selects the first forward server.
+ /// Some state handlers require a server to have been selected.
+ /// This selects a server without going through the state
+ /// transition(s) to do so.
+ bool selectFwdServer() {
+ if (getForwardDomain()) {
+ initServerSelection(getForwardDomain());
+ selectNextServer();
+ return (getCurrentServer());
+ }
+
+ return (false);
+ }
+
+ /// @brief Selects the first reverse server.
+ /// Some state handlers require a server to have been selected.
+ /// This selects a server without going through the state
+ /// transition(s) to do so.
+ bool selectRevServer() {
+ if (getReverseDomain()) {
+ initServerSelection(getReverseDomain());
+ selectNextServer();
+ return (getCurrentServer());
+ }
+
+ return (false);
+ }
+
+ /// @brief One-shot flag which will simulate sendUpdate failure if true.
+ bool simulate_send_exception_;
+
+ /// @brief One-shot flag which will simulate an exception when sendUpdate
+ /// failure if true.
+ bool simulate_build_request_exception_;
+
+ using StateModel::postNextEvent;
+ using StateModel::setState;
+ using StateModel::initDictionaries;
+ using NameRemoveTransaction::defineEvents;
+ using NameRemoveTransaction::verifyEvents;
+ using NameRemoveTransaction::defineStates;
+ using NameRemoveTransaction::verifyStates;
+ using NameRemoveTransaction::readyHandler;
+ using NameRemoveTransaction::selectingFwdServerHandler;
+ using NameRemoveTransaction::getCurrentServer;
+ using NameRemoveTransaction::removingFwdAddrsHandler;
+ using NameRemoveTransaction::setDnsUpdateStatus;
+ using NameRemoveTransaction::removingFwdRRsHandler;
+ using NameRemoveTransaction::selectingRevServerHandler;
+ using NameRemoveTransaction::removingRevPtrsHandler;
+ using NameRemoveTransaction::processRemoveOkHandler;
+ using NameRemoveTransaction::processRemoveFailedHandler;
+ using NameRemoveTransaction::buildRemoveFwdAddressRequest;
+ using NameRemoveTransaction::buildRemoveFwdRRsRequest;
+ using NameRemoveTransaction::buildRemoveRevPtrsRequest;
+};
+
+typedef boost::shared_ptr<NameRemoveStub> NameRemoveStubPtr;
+
+/// @brief Test fixture for testing NameRemoveTransaction
+///
+/// Note this class uses NameRemoveStub class to exercise non-public
+/// aspects of NameRemoveTransaction.
+class NameRemoveTransactionTest : public TransactionTest {
+public:
+ NameRemoveTransactionTest() {
+ }
+
+ virtual ~NameRemoveTransactionTest() {
+ }
+
+ /// @brief Creates a transaction which requests an IPv4 DNS update.
+ ///
+ /// The transaction is constructed around a predefined (i.e. "canned")
+ /// IPv4 NameChangeRequest. The request has both forward and reverse DNS
+ /// changes requested. Based upon the change mask, the transaction
+ /// will have either the forward, reverse, or both domains populated.
+ ///
+ /// @param change_mask determines which change directions are requested
+ NameRemoveStubPtr makeTransaction4(int change_mask) {
+ // Creates IPv4 remove request, forward, and reverse domains.
+ setupForIPv4Transaction(dhcp_ddns::CHG_REMOVE, change_mask);
+
+ // Now create the test transaction as would occur in update manager.
+ return (NameRemoveStubPtr(new NameRemoveStub(io_service_, ncr_,
+ forward_domain_,
+ reverse_domain_)));
+ }
+
+ /// @brief Creates a transaction which requests an IPv6 DNS update.
+ ///
+ /// The transaction is constructed around a predefined (i.e. "canned")
+ /// IPv6 NameChangeRequest. The request has both forward and reverse DNS
+ /// changes requested. Based upon the change mask, the transaction
+ /// will have either the forward, reverse, or both domains populated.
+ ///
+ /// @param change_mask determines which change directions are requested
+ NameRemoveStubPtr makeTransaction6(int change_mask) {
+ // Creates IPv6 remove request, forward, and reverse domains.
+ setupForIPv6Transaction(dhcp_ddns::CHG_REMOVE, change_mask);
+
+ // Now create the test transaction as would occur in update manager.
+ return (NameRemoveStubPtr(new NameRemoveStub(io_service_, ncr_,
+ forward_domain_,
+ reverse_domain_)));
+ }
+
+ /// @brief Create a test transaction at a known point in the state model.
+ ///
+ /// Method prepares a new test transaction and sets its state and next
+ /// event values to those given. This makes the transaction appear to
+ /// be at that point in the state model without having to transition it
+ /// through prerequisite states. It also provides the ability to set
+ /// which change directions are requested: forward change only, reverse
+ /// change only, or both.
+ ///
+ /// @param state value to set as the current state
+ /// @param event value to post as the next event
+ /// @param change_mask determines which change directions are requested
+ /// @param family selects between an IPv4 (AF_INET) and IPv6 (AF_INET6)
+ /// transaction.
+ NameRemoveStubPtr prepHandlerTest(unsigned int state, unsigned int event,
+ unsigned int change_mask
+ = FWD_AND_REV_CHG,
+ short family = AF_INET) {
+ NameRemoveStubPtr name_remove = (family == AF_INET ?
+ makeTransaction4(change_mask) :
+ makeTransaction6(change_mask));
+ name_remove->initDictionaries();
+ name_remove->postNextEvent(event);
+ name_remove->setState(state);
+ return (name_remove);
+ }
+
+};
+
+/// @brief Tests NameRemoveTransaction construction.
+/// This test verifies that:
+/// 1. Construction with invalid type of request
+/// 2. Valid construction functions properly
+TEST(NameRemoveTransaction, construction) {
+ IOServicePtr io_service(new isc::asiolink::IOService());
+
+ const char* msg_str =
+ "{"
+ " \"change_type\" : 0 , "
+ " \"forward_change\" : true , "
+ " \"reverse_change\" : true , "
+ " \"fqdn\" : \"example.com.\" , "
+ " \"ip_address\" : \"192.168.2.1\" , "
+ " \"dhcid\" : \"0102030405060708\" , "
+ " \"lease_expires_on\" : \"20130121132405\" , "
+ " \"lease_length\" : 1300 "
+ "}";
+
+ dhcp_ddns::NameChangeRequestPtr ncr;
+ DnsServerInfoStoragePtr servers;
+ DdnsDomainPtr forward_domain;
+ DdnsDomainPtr reverse_domain;
+ DdnsDomainPtr empty_domain;
+
+ ASSERT_NO_THROW(ncr = dhcp_ddns::NameChangeRequest::fromJSON(msg_str));
+ ASSERT_NO_THROW(forward_domain.reset(new DdnsDomain("*", "", servers)));
+ ASSERT_NO_THROW(reverse_domain.reset(new DdnsDomain("*", "", servers)));
+
+ // Verify that construction with wrong change type fails.
+ EXPECT_THROW(NameRemoveTransaction(io_service, ncr,
+ forward_domain, reverse_domain),
+ NameRemoveTransactionError);
+
+ // Verify that a valid construction attempt works.
+ ncr->setChangeType(isc::dhcp_ddns::CHG_REMOVE);
+ EXPECT_NO_THROW(NameRemoveTransaction(io_service, ncr,
+ forward_domain, reverse_domain));
+}
+
+/// @brief Tests event and state dictionary construction and verification.
+TEST_F(NameRemoveTransactionTest, dictionaryCheck) {
+ NameRemoveStubPtr name_remove;
+ ASSERT_NO_THROW(name_remove = makeTransaction4(FWD_AND_REV_CHG));
+ // Verify that the event and state dictionary validation fails prior
+ // dictionary construction.
+ ASSERT_THROW(name_remove->verifyEvents(), StateModelError);
+ ASSERT_THROW(name_remove->verifyStates(), StateModelError);
+
+ // Construct both dictionaries.
+ ASSERT_NO_THROW(name_remove->defineEvents());
+ ASSERT_NO_THROW(name_remove->defineStates());
+
+ // Verify both event and state dictionaries now pass validation.
+ ASSERT_NO_THROW(name_remove->verifyEvents());
+ ASSERT_NO_THROW(name_remove->verifyStates());
+}
+
+#if 0
+
+/// @brief Tests construction of a DNS update request for adding a forward
+/// dns entry.
+TEST_F(NameRemoveTransactionTest, buildForwardAdd) {
+ // Create a IPv4 forward add transaction.
+ // Verify the request builds without error.
+ // and then verify the request contents.
+ NameRemoveStubPtr name_remove;
+ ASSERT_NO_THROW(name_remove = makeTransaction4());
+ ASSERT_NO_THROW(name_remove->buildAddFwdAddressRequest());
+ checkForwardAddRequest(*name_remove);
+
+ // Create a IPv6 forward add transaction.
+ // Verify the request builds without error.
+ // and then verify the request contents.
+ ASSERT_NO_THROW(name_remove = makeTransaction6());
+ ASSERT_NO_THROW(name_remove->buildAddFwdAddressRequest());
+ checkForwardAddRequest(*name_remove);
+}
+
+/// @brief Tests construction of a DNS update request for replacing a forward
+/// dns entry.
+TEST_F(NameRemoveTransactionTest, buildReplaceFwdAddressRequest) {
+ // Create a IPv4 forward replace transaction.
+ // Verify the request builds without error.
+ // and then verify the request contents.
+ NameRemoveStubPtr name_remove;
+ ASSERT_NO_THROW(name_remove = makeTransaction4());
+ ASSERT_NO_THROW(name_remove->buildReplaceFwdAddressRequest());
+ checkForwardReplaceRequest(*name_remove);
+
+ // Create a IPv6 forward replace transaction.
+ // Verify the request builds without error.
+ // and then verify the request contents.
+ ASSERT_NO_THROW(name_remove = makeTransaction6());
+ ASSERT_NO_THROW(name_remove->buildReplaceFwdAddressRequest());
+ checkForwardReplaceRequest(*name_remove);
+}
+
+/// @brief Tests the construction of a DNS update request for replacing a
+/// reverse dns entry.
+TEST_F(NameRemoveTransactionTest, buildReplaceRevPtrsRequest) {
+ // Create a IPv4 reverse replace transaction.
+ // Verify the request builds without error.
+ // and then verify the request contents.
+ NameRemoveStubPtr name_remove;
+ ASSERT_NO_THROW(name_remove = makeTransaction4());
+ ASSERT_NO_THROW(name_remove->buildReplaceRevPtrsRequest());
+ checkReverseReplaceRequest(*name_remove);
+
+ // Create a IPv6 reverse replace transaction.
+ // Verify the request builds without error.
+ // and then verify the request contents.
+ ASSERT_NO_THROW(name_remove = makeTransaction6());
+ ASSERT_NO_THROW(name_remove->buildReplaceRevPtrsRequest());
+ checkReverseReplaceRequest(*name_remove);
+}
+#endif
+
+// Tests the readyHandler functionality.
+// It verifies behavior for the following scenarios:
+//
+// 1. Posted event is START_EVT and request includes only a forward change
+// 2. Posted event is START_EVT and request includes both a forward and a
+// reverse change
+// 3. Posted event is START_EVT and request includes only a reverse change
+// 4. Posted event is invalid
+//
+TEST_F(NameRemoveTransactionTest, readyHandler) {
+ NameRemoveStubPtr name_remove;
+
+ // Create a transaction which includes only a forward change.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::READY_ST,
+ StateModel::START_EVT, FORWARD_CHG));
+ // Run readyHandler.
+ EXPECT_NO_THROW(name_remove->readyHandler());
+
+ // Verify that a request requiring only a forward change, transitions to
+ // selecting a forward server.
+ EXPECT_EQ(NameChangeTransaction::SELECTING_FWD_SERVER_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT,
+ name_remove->getNextEvent());
+
+ // Create a transaction which includes both a forward and a reverse change.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::READY_ST,
+ StateModel::START_EVT, FWD_AND_REV_CHG));
+ // Run readyHandler.
+ EXPECT_NO_THROW(name_remove->readyHandler());
+
+ // Verify that a request requiring both forward and reverse, starts with
+ // the forward change by transitioning to selecting a forward server.
+ EXPECT_EQ(NameChangeTransaction::SELECTING_FWD_SERVER_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT,
+ name_remove->getNextEvent());
+
+ // Create and prep a reverse only transaction.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::READY_ST,
+ StateModel::START_EVT, REVERSE_CHG));
+ // Run readyHandler.
+ EXPECT_NO_THROW(name_remove->readyHandler());
+
+ // Verify that a request requiring only a reverse change, transitions to
+ // selecting a reverse server.
+ EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT,
+ name_remove->getNextEvent());
+
+ // Create and prep transaction, poised to run the handler but with an
+ // invalid event.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::READY_ST,
+ StateModel::NOP_EVT));
+
+ // Running the readyHandler should throw.
+ EXPECT_THROW(name_remove->readyHandler(), NameRemoveTransactionError);
+}
+
+
+// Tests the selectingFwdServerHandler functionality.
+// It verifies behavior for the following scenarios:
+//
+// 1. Posted event is SELECT_SERVER_EVT
+// 2. Posted event is SERVER_IO_ERROR_EVT
+// 3. Posted event is invalid
+//
+TEST_F(NameRemoveTransactionTest, selectingFwdServerHandler) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::
+ SELECTING_FWD_SERVER_ST,
+ NameChangeTransaction::SELECT_SERVER_EVT));
+
+ // Call selectingFwdServerHandler enough times to select all of the
+ // servers in it's current domain. The first time, it will be with
+ // next event of SELECT_SERVER_EVT. Thereafter it will be with a next
+ // event of SERVER_IO_ERROR_EVT.
+ int num_servers = name_remove->getForwardDomain()->getServers()->size();
+ for (int i = 0; i < num_servers; ++i) {
+ // Run selectingFwdServerHandler.
+ ASSERT_NO_THROW(name_remove->selectingFwdServerHandler())
+ << " num_servers: " << num_servers
+ << " selections: " << i;
+
+ // Verify that a server was selected.
+ ASSERT_TRUE(name_remove->getCurrentServer())
+ << " num_servers: " << num_servers << " selections: " << i;
+
+ // Verify that we transitioned correctly.
+ ASSERT_EQ(NameRemoveTransaction::REMOVING_FWD_ADDRS_ST,
+ name_remove->getCurrState())
+ << " num_servers: " << num_servers << " selections: " << i;
+ ASSERT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+ name_remove->getNextEvent())
+ << " num_servers: " << num_servers << " selections: " << i;
+
+ // Post a server IO error event. This simulates an IO error occuring
+ // and a need to select the new server.
+ ASSERT_NO_THROW(name_remove->postNextEvent(NameChangeTransaction::
+ SERVER_IO_ERROR_EVT))
+ << " num_servers: " << num_servers
+ << " selections: " << i;
+ }
+
+ // We should have exhausted the list of servers. Processing another
+ // SERVER_IO_ERROR_EVT should transition us to failure.
+ EXPECT_NO_THROW(name_remove->selectingFwdServerHandler());
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::NO_MORE_SERVERS_EVT,
+ name_remove->getNextEvent());
+
+ // Create and prep transaction, poised to run the handler but with an
+ // invalid event.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::
+ SELECTING_FWD_SERVER_ST,
+ StateModel::NOP_EVT));
+
+ // Running the handler should throw.
+ EXPECT_THROW(name_remove->selectingFwdServerHandler(),
+ NameRemoveTransactionError);
+}
+
+// ************************ addingFwdAddrHandler Tests *****************
+
+// Tests that removingFwdAddrsHandler rejects invalid events.
+TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_InvalidEvent) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler but with
+ // an invalid event.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_ADDRS_ST,
+ StateModel::NOP_EVT));
+
+ // Running the handler should throw.
+ EXPECT_THROW(name_remove->removingFwdAddrsHandler(),
+ NameRemoveTransactionError);
+}
+
+
+// Tests addingFwdAddrsHandler with the following scenario:
+//
+// The request includes only a forward change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The update request is sent without error.
+// A server response is received which indicates successful update
+//
+TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_FwdOnlyOK) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_ADDRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, FORWARD_CHG));
+
+ // Should not be an update message yet.
+ D2UpdateMessagePtr update_msg = name_remove->getDnsUpdateRequest();
+ ASSERT_FALSE(update_msg);
+
+ // At this point completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Run removingFwdAddrsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+ // Verify that an update message was constructed properly.
+ checkForwardRemoveAddrsRequest(*name_remove);
+
+ // Verify that we are still in this state and next event is NOP_EVT.
+ // This indicates we "sent" the message and are waiting for IO completion.
+ EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_ADDRS_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::NOP_EVT,
+ name_remove->getNextEvent());
+
+ // Simulate receiving a successful update response.
+ name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+ // Run removingFwdAddrsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+ // Completion flags should both still be false, as we are only partly
+ // done with forward updates.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Since we succeeded, we should now attempt to remove any remaining
+ // forward RRs.
+ // Verify that we transitioned correctly.
+ EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_RRS_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameRemoveTransaction::UPDATE_OK_EVT,
+ name_remove->getNextEvent());
+}
+
+// Tests addingFwdAddrsHandler with the following scenario:
+//
+// The request includes only a forward change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The update request is sent without error.
+// A server response is received which indicates FQDN is not in use.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_FqdnNotInUse) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_ADDRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, FORWARD_CHG));
+
+ // Run removingFwdAddrsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+ // Simulate receiving a FQDN not in use response.
+ name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NXDOMAIN());
+
+ // Run removingFwdAddrsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+ // Completion flags should both still be false, as we are only partly
+ // done with forward updates.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // There was no address RR to remove, but we will still make sure there
+ // are no other RRs for this FQDN.
+ // Verify that we transitioned correctly.
+ EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_RRS_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameRemoveTransaction::UPDATE_OK_EVT,
+ name_remove->getNextEvent());
+}
+
+
+// Tests removingFwdAddrsHandler with the following scenario:
+//
+// The request includes a forward and reverse change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The update request is sent without error.
+// A server response is received which indicates the update was rejected.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_OtherRcode) {
+ NameRemoveStubPtr name_remove;
+
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_ADDRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT));
+
+ // Select a server to satisfy log statements.
+ ASSERT_TRUE(name_remove->selectFwdServer());
+
+ // Run removingFwdAddrsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+ // Simulate receiving server rejection response. Per RFC, anything other
+ // than no error or FQDN not in use is failure. Arbitrarily choosing
+ // refused.
+ name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED());
+
+ // Run removingFwdAddrsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+ // Completion flags should still be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // We should have failed the transaction. Verify that we transitioned
+ // correctly.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+ name_remove->getNextEvent());
+}
+
+
+// Tests removingFwdAddrsHandler with the following scenario:
+//
+// The request includes a forward and reverse change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The update request send times out MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_Timeout) {
+ NameRemoveStubPtr name_remove;
+
+ // Create and prep a transaction, poised to run the handler.
+ // The log message issued when this test succeeds, displays the
+ // selected server, so we need to select a server before running this
+ // test.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_ADDRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT));
+
+ // Select a server to satisfy log statements.
+ ASSERT_TRUE(name_remove->selectFwdServer());
+
+ // Verify that we can make maximum number of update attempts permitted
+ // and then transition to selecting a new server.
+ int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
+ for (int i = 1; i <= max_tries; ++i) {
+ const D2UpdateMessagePtr prev_msg = name_remove->getDnsUpdateRequest();
+
+ // Run removingFwdAddrsHandler to send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+ const D2UpdateMessagePtr curr_msg = name_remove->getDnsUpdateRequest();
+ if (i == 1) {
+ // First time around we should build the message.
+ EXPECT_FALSE(prev_msg);
+ EXPECT_TRUE(curr_msg);
+ } else {
+ // Subsequent passes should reuse the request. We are only
+ // looking to check that we have not replaced the pointer value
+ // with a new pointer. This tests the on_entry() logic which
+ // clears the request ONLY upon initial entry into the state.
+ EXPECT_TRUE(prev_msg == curr_msg);
+ }
+
+ // Simulate a server IO timeout.
+ name_remove->setDnsUpdateStatus(DNSClient::TIMEOUT);
+ name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+ // Run removingFwdAddrsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ if (i < max_tries) {
+ // We should be ready to try again.
+ EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_ADDRS_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+ name_remove->getNextEvent());
+ } else {
+ // Server retries should be exhausted, time for a new server.
+ EXPECT_EQ(NameRemoveTransaction::SELECTING_FWD_SERVER_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
+ name_remove->getNextEvent());
+ }
+ }
+}
+
+// Tests removingFwdAddrsHandler with the following scenario:
+//
+// The request includes a forward and reverse change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The update request is sent but a corrupt response is received, this occurs
+// MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_InvalidResponse) {
+ NameRemoveStubPtr name_remove;
+
+ // Create and prep a transaction, poised to run the handler.
+ // The log message issued when this test succeeds, displays the
+ // selected server, so we need to select a server before running this
+ // test.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_ADDRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT));
+
+ // Select a server to satisfy log statements.
+ ASSERT_TRUE(name_remove->selectFwdServer());
+
+ // Verify that we can make maximum number of update attempts permitted
+ // and then transition to selecting a new server.
+ int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
+ for (int i = 1; i <= max_tries; ++i) {
+ // Run removingFwdAddrsHandler to construct send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+ // Simulate a corrupt server response.
+ name_remove->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE);
+ name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+ // Run removingFwdAddrsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ if (i < max_tries) {
+ // We should be ready to try again.
+ EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_ADDRS_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+ name_remove->getNextEvent());
+ } else {
+ // Server retries should be exhausted, time for a new server.
+ EXPECT_EQ(NameRemoveTransaction::SELECTING_FWD_SERVER_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
+ name_remove->getNextEvent());
+ }
+ }
+
+}
+
+// ************************ removingFwdRRsHandler Tests *****************
+
+// Tests that removingFwdRRsHandler rejects invalid events.
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_InvalidEvent) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler but with
+ // an invalid event.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_RRS_ST,
+ StateModel::NOP_EVT));
+
+ // Running the handler should throw.
+ EXPECT_THROW(name_remove->removingFwdRRsHandler(),
+ NameRemoveTransactionError);
+}
+
+// Tests removingFwdRRsHandler with the following scenario:
+//
+// The request includes only a forward change.
+// Initial posted event is UPDATE_OK_EVT.
+// The update request is sent without error.
+// A server response is received which indicates successful update.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_FwdOnlyOK) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_ADDRS_ST,
+ NameChangeTransaction::
+ UPDATE_OK_EVT, FORWARD_CHG));
+
+ // Should not be an update message yet.
+ D2UpdateMessagePtr update_msg = name_remove->getDnsUpdateRequest();
+ ASSERT_FALSE(update_msg);
+
+ // At this point completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Run removingFwdRRsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Verify that an update message was constructed properly.
+ checkForwardRemoveRRsRequest(*name_remove);
+
+ // Verify that we are still in this state and next event is NOP_EVT.
+ // This indicates we "sent" the message and are waiting for IO completion.
+ EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_ADDRS_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::NOP_EVT,
+ name_remove->getNextEvent());
+
+ // Simulate receiving a successful update response.
+ name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+ // Run removingFwdRRsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Forward completion should be true, reverse should be false.
+ EXPECT_TRUE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Since it is a forward only change, we should be done.
+ // Verify that we transitioned correctly.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
+ name_remove->getNextEvent());
+}
+
+// Tests removingFwdRRsHandler with the following scenario:
+//
+// The request includes only a forward change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The update request is sent without error.
+// A server response is received which indicates successful update.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_FwdOnlyOK2) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_ADDRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, FORWARD_CHG));
+
+ // Run removingFwdRRsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Simulate receiving a successful update response.
+ name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+ // Run removingFwdRRsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Forward completion should be true, reverse should be false.
+ EXPECT_TRUE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Since it is a forward only change, we should be done.
+ // Verify that we transitioned correctly.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
+ name_remove->getNextEvent());
+}
+
+
+// Tests removingFwdRRsHandler with the following scenario:
+//
+// The request includes a forward and reverse change.
+// Initial posted event is UPDATE_OK_EVT.
+// The update request is sent without error.
+// A server response is received which indicates successful update.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_FwdAndRevOK) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_ADDRS_ST,
+ NameChangeTransaction::
+ UPDATE_OK_EVT, FWD_AND_REV_CHG));
+
+ // Run removingFwdRRsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Simulate receiving a successful update response.
+ name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+ // Run removingFwdRRsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Forward change completion should be true, reverse flag should be false.
+ EXPECT_TRUE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Since the request also includes a reverse change we should
+ // be poised to start it. Verify that we transitioned correctly.
+ EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT,
+ name_remove->getNextEvent());
+}
+
+// Tests replacingFwdAddrsHandler with the following scenario:
+//
+// The request includes a forward and reverse change.
+// Initial posted event is UPDATE_OK_EVT.
+// The update request is sent without error.
+// A server response is received which indicates the FQDN is NOT in use.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_FqdnNotInUse) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_ADDRS_ST,
+ NameChangeTransaction::
+ UPDATE_OK_EVT, FORWARD_CHG));
+
+ // Run removingFwdRRsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Simulate receiving a FQDN not in use response.
+ name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NXDOMAIN());
+
+ // Run removingFwdRRsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Forwad completion flag should be true, reverse should still be false.
+ EXPECT_TRUE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // The FQDN is no longer in use, RFC is unclear about this,
+ // but we will treat this as success.
+ // Since it is a forward only change, we should be done.
+ // Verify that we transitioned correctly.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
+ name_remove->getNextEvent());
+}
+
+// Tests removingFwdRRsHandler with the following scenario:
+//
+// The request includes a forward and reverse change.
+// The update request is sent without error.
+// A server response is received which indicates the update was rejected.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_OtherRcode) {
+ NameRemoveStubPtr name_remove;
+ // Create the transaction.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_ADDRS_ST,
+ NameChangeTransaction::
+ UPDATE_OK_EVT, FWD_AND_REV_CHG));
+
+ // Select a server to satisfy log statements.
+ ASSERT_TRUE(name_remove->selectFwdServer());
+
+ // Run removingFwdRRsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Simulate receiving server rejection response. Per RFC, anything other
+ // than no error is failure (we are also treating FQDN not in use is
+ // success). Arbitrarily choosing refused.
+ name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED());
+
+ // Run removingFwdRRsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Completion flags should still be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // We should have failed the transaction. Verifiy that we transitioned
+ // correctly.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+ name_remove->getNextEvent());
+}
+
+// Tests removingFwdRRsHandler with the following scenario:
+//
+// The request includes a forward and reverse change.
+// Initial posted event is UPDATE_OK_EVT.
+// The update request send times out MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_Timeout) {
+ NameRemoveStubPtr name_remove;
+
+ // Create the transaction.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_RRS_ST,
+ NameChangeTransaction::
+ UPDATE_OK_EVT, FWD_AND_REV_CHG));
+
+ // Select a server to satisfy log statements.
+ ASSERT_TRUE(name_remove->selectFwdServer());
+
+ // Verify that we can make maximum number of update attempts permitted
+ // and then transition to selecting a new server.
+ int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
+ for (int i = 1; i <= max_tries; ++i) {
+ const D2UpdateMessagePtr prev_msg = name_remove->getDnsUpdateRequest();
+
+ // Run removingFwdRRsHandler to send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ const D2UpdateMessagePtr curr_msg = name_remove->getDnsUpdateRequest();
+ if (i == 1) {
+ // First time around we should build the message.
+ EXPECT_FALSE(prev_msg);
+ EXPECT_TRUE(curr_msg);
+ } else {
+ // Subsequent passes should reuse the request. We are only
+ // looking to check that we have not replaced the pointer value
+ // with a new pointer. This tests the on_entry() logic which
+ // clears the request ONLY upon initial entry into the state.
+ EXPECT_TRUE(prev_msg == curr_msg);
+ }
+
+ // Simulate a server IO timeout.
+ name_remove->setDnsUpdateStatus(DNSClient::TIMEOUT);
+ name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+ // Run removingFwdRRsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ if (i < max_tries) {
+ // We should be ready to try again.
+ EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_RRS_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+ name_remove->getNextEvent());
+ } else {
+ // Server retries should be exhausted.
+ // We should abandon the transaction.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
+ name_remove->getNextEvent());
+ }
+ }
+}
+
+// Tests removingFwdRRsHandler with the following scenario:
+//
+// The request includes a forward and reverse change.
+// Initial posted event is UPDATE_OK_EVT.
+// The update request is sent but a corrupt response is received, this occurs
+// MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_InvalidResponse) {
+ NameRemoveStubPtr name_remove;
+
+ // Create the transaction.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_RRS_ST,
+ NameChangeTransaction::
+ UPDATE_OK_EVT, FWD_AND_REV_CHG));
+
+ // Select a server to satisfy log statements.
+ ASSERT_TRUE(name_remove->selectFwdServer());
+
+ // Verify that we can make maximum number of update attempts permitted
+ // and then transition to selecting a new server.
+ int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
+ for (int i = 1; i <= max_tries; ++i) {
+ const D2UpdateMessagePtr prev_msg = name_remove->getDnsUpdateRequest();
+
+ // Run removingFwdRRsHandler to send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ const D2UpdateMessagePtr curr_msg = name_remove->getDnsUpdateRequest();
+ if (i == 1) {
+ // First time around we should build the message.
+ EXPECT_FALSE(prev_msg);
+ EXPECT_TRUE(curr_msg);
+ } else {
+ // Subsequent passes should reuse the request. We are only
+ // looking to check that we have not replaced the pointer value
+ // with a new pointer. This tests the on_entry() logic which
+ // clears the request ONLY upon initial entry into the state.
+ EXPECT_TRUE(prev_msg == curr_msg);
+ }
+
+ // Simulate a corrupt server response.
+ name_remove->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE);
+ name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+ // Run removingFwdRRsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ if (i < max_tries) {
+ // We should be ready to try again.
+ EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_RRS_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+ name_remove->getNextEvent());
+ } else {
+ // Server retries should be exhausted.
+ // We should abandon the transaction.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
+ name_remove->getNextEvent());
+ }
+ }
+}
+
+
+// Tests the selectingRevServerHandler functionality.
+// It verifies behavior for the following scenarios:
+//
+// 1. Posted event is SELECT_SERVER_EVT
+// 2. Posted event is SERVER_IO_ERROR_EVT
+// 3. Posted event is invalid
+//
+TEST_F(NameRemoveTransactionTest, selectingRevServerHandler) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::
+ SELECTING_REV_SERVER_ST,
+ NameChangeTransaction::SELECT_SERVER_EVT));
+
+ // Call selectingRevServerHandler enough times to select all of the
+ // servers in it's current domain. The first time, it will be with
+ // next event of SELECT_SERVER_EVT. Thereafter it will be with a next
+ // event of SERVER_IO_ERROR_EVT.
+ int num_servers = name_remove->getReverseDomain()->getServers()->size();
+ for (int i = 0; i < num_servers; ++i) {
+ // Run selectingRevServerHandler.
+ ASSERT_NO_THROW(name_remove->selectingRevServerHandler())
+ << " num_servers: " << num_servers
+ << " selections: " << i;
+
+ // Verify that a server was selected.
+ ASSERT_TRUE(name_remove->getCurrentServer())
+ << " num_servers: " << num_servers
+ << " selections: " << i;
+
+ // Verify that we transitioned correctly.
+ ASSERT_EQ(NameRemoveTransaction::REMOVING_REV_PTRS_ST,
+ name_remove->getCurrState())
+ << " num_servers: " << num_servers << " selections: " << i;
+ ASSERT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+ name_remove->getNextEvent())
+ << " num_servers: " << num_servers << " selections: " << i;
+
+ // Post a server IO error event. This simulates an IO error occuring
+ // and a need to select the new server.
+ ASSERT_NO_THROW(name_remove->postNextEvent(NameChangeTransaction::
+ SERVER_IO_ERROR_EVT))
+ << " num_servers: " << num_servers
+ << " selections: " << i;
+ }
+
+ // We should have exhausted the list of servers. Processing another
+ // SERVER_IO_ERROR_EVT should transition us to failure.
+ EXPECT_NO_THROW(name_remove->selectingRevServerHandler());
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::NO_MORE_SERVERS_EVT,
+ name_remove->getNextEvent());
+
+ // Create and prep transaction, poised to run the handler but with an
+ // invalid event.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::
+ SELECTING_REV_SERVER_ST,
+ StateModel::NOP_EVT));
+
+ // Running the handler should throw.
+ EXPECT_THROW(name_remove->selectingRevServerHandler(),
+ NameRemoveTransactionError);
+}
+
+//************************** replacingRevPtrsHandler tests *****************
+
+// Tests that removingRevPtrsHandler rejects invalid events.
+TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_InvalidEvent) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler but with
+ // an invalid event.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_REV_PTRS_ST,
+ StateModel::NOP_EVT));
+
+ // Running the handler should throw.
+ EXPECT_THROW(name_remove->removingRevPtrsHandler(),
+ NameRemoveTransactionError);
+}
+
+// Tests replacingRevPtrsHandler with the following scenario:
+//
+// The request includes only a reverse change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The update request is sent without error.
+// A server response is received which indicates successful update.
+//
+TEST_F(NameRemoveTransactionTest, replacingRevPtrsHandler_RevOnlyOK) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_REV_PTRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, REVERSE_CHG));
+
+ // Should not be an update message yet.
+ D2UpdateMessagePtr update_msg = name_remove->getDnsUpdateRequest();
+ ASSERT_FALSE(update_msg);
+
+ // At this point completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Run removingRevPtrsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+ // Verify that an update message was constructed properly.
+ checkReverseRemoveRequest(*name_remove);
+
+ // Verify that we are still in this state and next event is NOP_EVT.
+ // This indicates we "sent" the message and are waiting for IO completion.
+ EXPECT_EQ(NameRemoveTransaction::REMOVING_REV_PTRS_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(StateModel::NOP_EVT,
+ name_remove->getNextEvent());
+
+ // Simulate receiving a successful update response.
+ name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+ // Run removingRevPtrsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+ // Forward completion should be false, reverse should be true.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_TRUE(name_remove->getReverseChangeCompleted());
+
+ // Since it is a reverse change, we should be done.
+ // Verify that we transitioned correctly.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
+ name_remove->getNextEvent());
+}
+
+// Tests replacingRevPtrsHandler with the following scenario:
+//
+// The request includes only a reverse change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The update request is sent without error.
+// A server response is received which indicates FQDN is NOT in use.
+//
+TEST_F(NameRemoveTransactionTest, replacingRevPtrsHandler_FqdnNotInUse) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_REV_PTRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, REVERSE_CHG));
+
+ // Should not be an update message yet.
+ D2UpdateMessagePtr update_msg = name_remove->getDnsUpdateRequest();
+ ASSERT_FALSE(update_msg);
+
+ // At this point completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Run removingRevPtrsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+ // Verify that an update message was constructed properly.
+ checkReverseRemoveRequest(*name_remove);
+
+ // Verify that we are still in this state and next event is NOP_EVT.
+ // This indicates we "sent" the message and are waiting for IO completion.
+ EXPECT_EQ(NameRemoveTransaction::REMOVING_REV_PTRS_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(StateModel::NOP_EVT,
+ name_remove->getNextEvent());
+
+ // Simulate receiving a FQDN not in use response.
+ name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NXDOMAIN());
+
+ // Run removingRevPtrsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+ // Forward completion should be false, reverse should be true.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_TRUE(name_remove->getReverseChangeCompleted());
+
+ // Since it is a reverse change, we should be done.
+ // Verify that we transitioned correctly.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
+ name_remove->getNextEvent());
+}
+
+// Tests removingRevPtrsHandler with the following scenario:
+//
+// The request includes only a reverse change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The update request is sent without error.
+// A server response is received which indicates the update was rejected.
+//
+TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_OtherRcode) {
+ NameRemoveStubPtr name_remove;
+ // Create the transaction.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_REV_PTRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, REVERSE_CHG));
+
+ // Select a server to satisfy log statements.
+ ASSERT_TRUE(name_remove->selectRevServer());
+
+ // Run removingRevPtrsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+ // Simulate receiving server rejection response. Per RFC, anything other
+ // than no error is failure. Arbitrarily choosing refused.
+ name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED());
+
+ // Run removingRevPtrsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+ // Completion flags should still be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // We should have failed the transaction. Verify that we transitioned
+ // correctly.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+ name_remove->getNextEvent());
+}
+
+// Tests removingRevPtrsHandler with the following scenario:
+//
+// The request includes only a reverse change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The update request send times out MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_Timeout) {
+ NameRemoveStubPtr name_remove;
+ // Create the transaction.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_REV_PTRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, REVERSE_CHG));
+
+ // Select a server to satisfy log statements.
+ ASSERT_TRUE(name_remove->selectRevServer());
+
+ // Verify that we can make maximum number of update attempts permitted
+ // and then transition to selecting a new server.
+ int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
+ for (int i = 1; i <= max_tries; ++i) {
+ const D2UpdateMessagePtr prev_msg = name_remove->getDnsUpdateRequest();
+
+ // Run removingRevPtrsHandler to send the request.
+ EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+ const D2UpdateMessagePtr curr_msg = name_remove->getDnsUpdateRequest();
+ if (i == 1) {
+ // First time around we should build the message.
+ EXPECT_FALSE(prev_msg);
+ EXPECT_TRUE(curr_msg);
+ } else {
+ // Subsequent passes should reuse the request. We are only
+ // looking to check that we have not replaced the pointer value
+ // with a new pointer. This tests the on_entry() logic which
+ // clears the request ONLY upon initial entry into the state.
+ EXPECT_TRUE(prev_msg == curr_msg);
+ }
+
+ // Simulate a server IO timeout.
+ name_remove->setDnsUpdateStatus(DNSClient::TIMEOUT);
+ name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+ // Run removingRevPtrsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ if (i < max_tries) {
+ // We should be ready to try again.
+ EXPECT_EQ(NameRemoveTransaction::REMOVING_REV_PTRS_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+ name_remove->getNextEvent());
+ } else {
+ // Server retries should be exhausted, time for a new server.
+ EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
+ name_remove->getNextEvent());
+ }
+ }
+}
+
+
+// Tests removingRevPtrsHandler with the following scenario:
+//
+// The request includes only a reverse change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The update request is sent but a corrupt response is received, this occurs
+// MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_CorruptResponse) {
+ NameRemoveStubPtr name_remove;
+ // Create the transaction.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_REV_PTRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, REVERSE_CHG));
+
+ // Select a server to satisfy log statements.
+ ASSERT_TRUE(name_remove->selectRevServer());
+
+ // Verify that we can make maximum number of update attempts permitted
+ // and then transition to selecting a new server.
+ int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;
+ for (int i = 1; i <= max_tries; ++i) {
+ const D2UpdateMessagePtr prev_msg = name_remove->getDnsUpdateRequest();
+
+ // Run removingRevPtrsHandler to send the request.
+ EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+ const D2UpdateMessagePtr curr_msg = name_remove->getDnsUpdateRequest();
+ if (i == 1) {
+ // First time around we should build the message.
+ EXPECT_FALSE(prev_msg);
+ EXPECT_TRUE(curr_msg);
+ } else {
+ // Subsequent passes should reuse the request. We are only
+ // looking to check that we have not replaced the pointer value
+ // with a new pointer. This tests the on_entry() logic which
+ // clears the request ONLY upon initial entry into the state.
+ EXPECT_TRUE(prev_msg == curr_msg);
+ }
+
+ // Simulate a server corrupt response.
+ name_remove->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE);
+ name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+ // Run removingRevPtrsHandler again to process the response.
+ EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ if (i < max_tries) {
+ // We should be ready to try again.
+ EXPECT_EQ(NameRemoveTransaction::REMOVING_REV_PTRS_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+ name_remove->getNextEvent());
+ } else {
+ // Server retries should be exhausted, time for a new server.
+ EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
+ name_remove->getNextEvent());
+ }
+ }
+}
+
+// Tests the processRemoveOkHandler functionality.
+// It verifies behavior for the following scenarios:
+//
+// 1. Posted event is UPDATE_OK_EVT
+// 2. Posted event is invalid
+//
+TEST_F(NameRemoveTransactionTest, processRemoveOkHandler) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+ NameChangeTransaction::UPDATE_OK_EVT));
+ // Run processRemoveOkHandler.
+ EXPECT_NO_THROW(name_remove->processRemoveOkHandler());
+
+ // Verify that a server was selected.
+ EXPECT_EQ(dhcp_ddns::ST_COMPLETED, name_remove->getNcrStatus());
+
+ // Verify that the model has ended.
+ EXPECT_EQ(StateModel::END_ST, name_remove->getCurrState());
+ EXPECT_EQ(StateModel::END_EVT, name_remove->getNextEvent());
+
+
+ // Create and prep transaction, poised to run the handler but with an
+ // invalid event.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+ StateModel::NOP_EVT));
+ // Running the handler should throw.
+ EXPECT_THROW(name_remove->processRemoveOkHandler(),
+ NameRemoveTransactionError);
+}
+
+// Tests the processRemoveFailedHandler functionality.
+// It verifies behavior for the following scenarios:
+//
+// 1. Posted event is UPDATE_FAILED_EVT
+// 2. Posted event is invalid
+//
+TEST_F(NameRemoveTransactionTest, processRemoveFailedHandler) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::
+ PROCESS_TRANS_FAILED_ST,
+ NameChangeTransaction::UPDATE_FAILED_EVT));
+ // Run processRemoveFailedHandler.
+ EXPECT_NO_THROW(name_remove->processRemoveFailedHandler());
+
+ // Verify that a server was selected.
+ EXPECT_EQ(dhcp_ddns::ST_FAILED, name_remove->getNcrStatus());
+
+ // Verify that the model has ended. (Remember, the transaction failed NOT
+ // the model. The model should have ended normally.)
+ EXPECT_EQ(StateModel::END_ST, name_remove->getCurrState());
+ EXPECT_EQ(StateModel::END_EVT, name_remove->getNextEvent());
+
+
+ // Create and prep transaction, poised to run the handler but with an
+ // invalid event.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::
+ PROCESS_TRANS_FAILED_ST,
+ StateModel::NOP_EVT));
+ // Running the handler should throw.
+ EXPECT_THROW(name_remove->processRemoveFailedHandler(),
+ NameRemoveTransactionError);
+}
+
+// Tests the processRemoveFailedHandler functionality.
+// It verifies behavior for posted event of NO_MORE_SERVERS_EVT.
+TEST_F(NameRemoveTransactionTest, processRemoveFailedHandler_NoMoreServers) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::
+ PROCESS_TRANS_FAILED_ST,
+ NameChangeTransaction::
+ NO_MORE_SERVERS_EVT));
+
+ // Run processRemoveFailedHandler.
+ EXPECT_NO_THROW(name_remove->processRemoveFailedHandler());
+
+ // Verify that a server was selected.
+ EXPECT_EQ(dhcp_ddns::ST_FAILED, name_remove->getNcrStatus());
+
+ // Verify that the model has ended. (Remember, the transaction failed NOT
+ // the model. The model should have ended normally.)
+ EXPECT_EQ(StateModel::END_ST, name_remove->getCurrState());
+ EXPECT_EQ(StateModel::END_EVT, name_remove->getNextEvent());
+}
+
+// Tests the processRemoveFailedHandler functionality.
+// It verifies behavior for posted event of SERVER_IO_ERROR_EVT.
+TEST_F(NameRemoveTransactionTest, processRemoveFailedHandler_ServerIOError) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameChangeTransaction::
+ PROCESS_TRANS_FAILED_ST,
+ NameChangeTransaction::
+ SERVER_IO_ERROR_EVT));
+
+ // Run processRemoveFailedHandler.
+ EXPECT_NO_THROW(name_remove->processRemoveFailedHandler());
+
+ // Verify that a server was selected.
+ EXPECT_EQ(dhcp_ddns::ST_FAILED, name_remove->getNcrStatus());
+
+ // Verify that the model has ended. (Remember, the transaction failed NOT
+ // the model. The model should have ended normally.)
+ EXPECT_EQ(StateModel::END_ST, name_remove->getCurrState());
+ EXPECT_EQ(StateModel::END_EVT, name_remove->getNextEvent());
+}
+
+// Tests removingFwdAddrsHandler with the following scenario:
+//
+// The request includes only a forward change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The send update request fails due to an unexpected exception.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_sendUpdateException) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_ADDRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, FORWARD_CHG));
+
+ name_remove->simulate_send_exception_ = true;
+
+ // Run removingFwdAddrsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Since IO exceptions should be gracefully handled, any that occur
+ // are unanticipated, and deemed unrecoverable, so the transaction should
+ // be transitioned to failure.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+ name_remove->getNextEvent());
+}
+
+
+// Tests removingFwdRRsHandler with the following scenario:
+//
+// The request includes only a forward change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The send update request fails due to an unexpected exception.
+//
+TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_SendUpdateException) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_RRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, FORWARD_CHG));
+
+ name_remove->simulate_send_exception_ = true;
+
+ // Run removingFwdRRsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Since IO exceptions should be gracefully handled, any that occur
+ // are unanticipated, and deemed unrecoverable, so the transaction should
+ // be transitioned to failure.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+ name_remove->getNextEvent());
+}
+
+// Tests removingRevPtrHandler with the following scenario:
+//
+// The request includes only a reverse change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The send update request fails due to an unexpected exception.
+//
+TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_SendUpdateException) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_REV_PTRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, REVERSE_CHG));
+
+ name_remove->simulate_send_exception_ = true;
+
+ // Run removingRevPtrsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Since IO exceptions should be gracefully handled, any that occur
+ // are unanticipated, and deemed unrecoverable, so the transaction should
+ // be transitioned to failure.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+ name_remove->getNextEvent());
+}
+
+// Tests removingFwdAddrsHandler with the following scenario:
+//
+// The request includes only a forward change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The request build fails due to an unexpected exception.
+//
+TEST_F(NameRemoveTransactionTest,
+ removingFwdAddrsHandler_BuildRequestException) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_ADDRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, FORWARD_CHG));
+
+ // Set the one-shot exception simulation flag.
+ name_remove->simulate_build_request_exception_ = true;
+
+ // Run removingFwdAddrsHandler to construct and send the request.
+ // This should fail with a build request throw which should be caught
+ // in the state handler.
+ ASSERT_NO_THROW(name_remove->removingFwdAddrsHandler());
+
+ // Verify we did not attempt to send anything.
+ EXPECT_EQ(0, name_remove->getUpdateAttempts());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Since IO exceptions should be gracefully handled, any that occur
+ // are unanticipated, and deemed unrecoverable, so the transaction should
+ // be transitioned to failure.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+ name_remove->getNextEvent());
+}
+
+// Tests removingFwdRRsHandler with the following scenario:
+//
+// The request includes only a forward change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The request build fails due to an unexpected exception.
+//
+TEST_F(NameRemoveTransactionTest,
+ removingFwdRRsHandler_BuildRequestException) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_FWD_RRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, FORWARD_CHG));
+
+ // Set the one-shot exception simulation flag.
+ name_remove->simulate_build_request_exception_ = true;
+
+ // Run removingFwdRRsHandler to construct and send the request.
+ // This should fail with a build request throw which should be caught
+ // in the state handler.
+ ASSERT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Verify we did not attempt to send anything.
+ EXPECT_EQ(0, name_remove->getUpdateAttempts());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Since IO exceptions should be gracefully handled, any that occur
+ // are unanticipated, and deemed unrecoverable, so the transaction should
+ // be transitioned to failure.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+ name_remove->getNextEvent());
+}
+
+// Tests removingRevPTRsHandler with the following scenario:
+//
+// The request includes only a forward change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The request build fails due to an unexpected exception.
+//
+TEST_F(NameRemoveTransactionTest,
+ removingRevPTRsHandler_BuildRequestException) {
+ NameRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(name_remove =
+ prepHandlerTest(NameRemoveTransaction::
+ REMOVING_REV_PTRS_ST,
+ NameChangeTransaction::
+ SERVER_SELECTED_EVT, FORWARD_CHG));
+
+ // Set the one-shot exception simulation flag.
+ name_remove->simulate_build_request_exception_ = true;
+
+ // Run removingRevPtrsHandler to construct and send the request.
+ // This should fail with a build request throw which should be caught
+ // in the state handler.
+ ASSERT_NO_THROW(name_remove->removingRevPtrsHandler());
+
+ // Verify we did not attempt to send anything.
+ EXPECT_EQ(0, name_remove->getUpdateAttempts());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_remove->getForwardChangeCompleted());
+ EXPECT_FALSE(name_remove->getReverseChangeCompleted());
+
+ // Since IO exceptions should be gracefully handled, any that occur
+ // are unanticipated, and deemed unrecoverable, so the transaction should
+ // be transitioned to failure.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_remove->getCurrState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+ name_remove->getNextEvent());
+}
+
+}
diff --git a/src/bin/d2/tests/nc_test_utils.cc b/src/bin/d2/tests/nc_test_utils.cc
index 2df18e9..a6f21aa 100644
--- a/src/bin/d2/tests/nc_test_utils.cc
+++ b/src/bin/d2/tests/nc_test_utils.cc
@@ -29,6 +29,8 @@ namespace d2 {
const char* TEST_DNS_SERVER_IP = "127.0.0.1";
size_t TEST_DNS_SERVER_PORT = 5301;
+//*************************** FauxServer class ***********************
+
FauxServer::FauxServer(asiolink::IOService& io_service,
asiolink::IOAddress& address, size_t port)
:io_service_(io_service), address_(address), port_(port),
@@ -136,6 +138,135 @@ FauxServer::requestHandler(const asio::error_code& error,
}
}
+//********************** TransactionTest class ***********************
+
+const unsigned int TransactionTest::FORWARD_CHG = 0x01;
+const unsigned int TransactionTest::REVERSE_CHG = 0x02;
+const unsigned int TransactionTest::FWD_AND_REV_CHG = REVERSE_CHG | FORWARD_CHG;
+
+TransactionTest::TransactionTest()
+ : io_service_(new isc::asiolink::IOService()), ncr_(),
+ timer_(*io_service_), run_time_(0) {
+}
+
+TransactionTest::~TransactionTest() {
+}
+
+void
+TransactionTest::runTimedIO(int run_time) {
+ run_time_ = run_time;
+ timer_.setup(boost::bind(&TransactionTest::timesUp, this), run_time_);
+ io_service_->run();
+}
+
+void
+TransactionTest::timesUp() {
+ io_service_->stop();
+ FAIL() << "Test Time: " << run_time_ << " expired";
+}
+
+void
+TransactionTest::setupForIPv4Transaction(dhcp_ddns::NameChangeType chg_type,
+ int change_mask) {
+ const char* msg_str =
+ "{"
+ " \"change_type\" : 0 , "
+ " \"forward_change\" : true , "
+ " \"reverse_change\" : true , "
+ " \"fqdn\" : \"my.forward.example.com.\" , "
+ " \"ip_address\" : \"192.168.2.1\" , "
+ " \"dhcid\" : \"0102030405060708\" , "
+ " \"lease_expires_on\" : \"20130121132405\" , "
+ " \"lease_length\" : 1300 "
+ "}";
+
+ // Create NameChangeRequest from JSON string.
+ ncr_ = dhcp_ddns::NameChangeRequest::fromJSON(msg_str);
+
+ // Set the change type.
+ ncr_->setChangeType(chg_type);
+
+ // If the change mask does not include a forward change clear the
+ // forward domain; otherwise create the domain and its servers.
+ if (!(change_mask & FORWARD_CHG)) {
+ ncr_->setForwardChange(false);
+ forward_domain_.reset();
+ } else {
+ // Create the forward domain and then its servers.
+ forward_domain_ = makeDomain("example.com.");
+ addDomainServer(forward_domain_, "forward.example.com",
+ "127.0.0.1", 5301);
+ addDomainServer(forward_domain_, "forward2.example.com",
+ "127.0.0.1", 5302);
+ }
+
+ // If the change mask does not include a reverse change clear the
+ // reverse domain; otherwise create the domain and its servers.
+ if (!(change_mask & REVERSE_CHG)) {
+ ncr_->setReverseChange(false);
+ reverse_domain_.reset();
+ } else {
+ // Create the reverse domain and its server.
+ reverse_domain_ = makeDomain("2.168.192.in.addr.arpa.");
+ addDomainServer(reverse_domain_, "reverse.example.com",
+ "127.0.0.1", 5301);
+ addDomainServer(reverse_domain_, "reverse2.example.com",
+ "127.0.0.1", 5302);
+ }
+}
+
+void
+TransactionTest::setupForIPv6Transaction(dhcp_ddns::NameChangeType chg_type,
+ int change_mask) {
+ const char* msg_str =
+ "{"
+ " \"change_type\" : 0 , "
+ " \"forward_change\" : true , "
+ " \"reverse_change\" : true , "
+ " \"fqdn\" : \"my6.forward.example.com.\" , "
+ " \"ip_address\" : \"2001:1::100\" , "
+ " \"dhcid\" : \"0102030405060708\" , "
+ " \"lease_expires_on\" : \"20130121132405\" , "
+ " \"lease_length\" : 1300 "
+ "}";
+
+ // Create NameChangeRequest from JSON string.
+ ncr_ = makeNcrFromString(msg_str);
+
+ // Set the change type.
+ ncr_->setChangeType(chg_type);
+
+ // If the change mask does not include a forward change clear the
+ // forward domain; otherwise create the domain and its servers.
+ if (!(change_mask & FORWARD_CHG)) {
+ ncr_->setForwardChange(false);
+ forward_domain_.reset();
+ } else {
+ // Create the forward domain and then its servers.
+ forward_domain_ = makeDomain("example.com.");
+ addDomainServer(forward_domain_, "fwd6-server.example.com",
+ "::1", 5301);
+ addDomainServer(forward_domain_, "fwd6-server2.example.com",
+ "::1", 5302);
+ }
+
+ // If the change mask does not include a reverse change clear the
+ // reverse domain; otherwise create the domain and its servers.
+ if (!(change_mask & REVERSE_CHG)) {
+ ncr_->setReverseChange(false);
+ reverse_domain_.reset();
+ } else {
+ // Create the reverse domain and its server.
+ reverse_domain_ = makeDomain("1.2001.ip6.arpa.");
+ addDomainServer(reverse_domain_, "rev6-server.example.com",
+ "::1", 5301);
+ addDomainServer(reverse_domain_, "rev6-server2.example.com",
+ "::1", 5302);
+ }
+}
+
+
+//********************** Functions ****************************
void
checkRRCount(const D2UpdateMessagePtr& request,
@@ -390,5 +521,33 @@ void checkReverseReplaceRequest(NameChangeTransaction& tran) {
ASSERT_NO_THROW(request->toWire(renderer));
}
+void checkForwardRemoveAddrsRequest(NameChangeTransaction& tran) {
+ const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest();
+ ASSERT_TRUE(request);
+
+ // Safety check.
+ dhcp_ddns::NameChangeRequestPtr ncr = tran.getNcr();
+ ASSERT_TRUE(ncr);
+}
+
+void checkForwardRemoveRRsRequest(NameChangeTransaction& tran) {
+ const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest();
+ ASSERT_TRUE(request);
+
+ // Safety check.
+ dhcp_ddns::NameChangeRequestPtr ncr = tran.getNcr();
+ ASSERT_TRUE(ncr);
+}
+
+void checkReverseRemoveRequest(NameChangeTransaction& tran) {
+ const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest();
+ ASSERT_TRUE(request);
+
+ // Safety check.
+ dhcp_ddns::NameChangeRequestPtr ncr = tran.getNcr();
+ ASSERT_TRUE(ncr);
+}
+
+
}; // namespace isc::d2
}; // namespace isc
diff --git a/src/bin/d2/tests/nc_test_utils.h b/src/bin/d2/tests/nc_test_utils.h
index 7cffd05..3c16cce 100644
--- a/src/bin/d2/tests/nc_test_utils.h
+++ b/src/bin/d2/tests/nc_test_utils.h
@@ -21,6 +21,7 @@
#include <asio/ip/udp.hpp>
#include <asio/socket_base.hpp>
+#include <gtest/gtest.h>
namespace isc {
namespace d2 {
@@ -94,6 +95,63 @@ public:
const dns::Rcode& response_rcode);
};
+/// @brief Base class Test fixture for testing transactions.
+class TransactionTest : public ::testing::Test {
+public:
+ IOServicePtr io_service_;
+ dhcp_ddns::NameChangeRequestPtr ncr_;
+ DdnsDomainPtr forward_domain_;
+ DdnsDomainPtr reverse_domain_;
+ asiolink::IntervalTimer timer_;
+ int run_time_;
+
+ static const unsigned int FORWARD_CHG;
+ static const unsigned int REVERSE_CHG;
+ static const unsigned int FWD_AND_REV_CHG;
+
+ TransactionTest();
+ virtual ~TransactionTest();
+
+ /// @brief Run the IO service for no more than a given amount of time.
+ ///
+ /// Uses an IntervalTimer to interrupt the invocation of IOService run(),
+ /// after the given number of milliseconds elapse. The timer executes
+ /// the timesUp() method if it expires.
+ ///
+ /// @param run_time amount of time in milliseconds to allow run to execute.
+ void runTimedIO(int run_time);
+
+ /// @brief IO Timer expiration handler
+ ///
+ /// Stops the IOSerivce and fails the current test.
+ virtual void timesUp();
+
+ /// @todo
+ /// @brief Creates a transaction which requests an IPv4 DNS update.
+ ///
+ /// The transaction is constructed around a predefined (i.e. "canned")
+ /// IPv4 NameChangeRequest. The request has both forward and reverse DNS
+ /// changes requested. Based upon the change mask, the transaction
+ /// will have either the forward, reverse, or both domains populated.
+ ///
+ /// @param change_mask determines which change directions are requested
+ void setupForIPv4Transaction(dhcp_ddns::NameChangeType chg_type,
+ int change_mask);
+
+ /// @todo
+ /// @brief Creates a transaction which requests an IPv6 DNS update.
+ ///
+ /// The transaction is constructed around a predefined (i.e. "canned")
+ /// IPv6 NameChangeRequest. The request has both forward and reverse DNS
+ /// changes requested. Based upon the change mask, the transaction
+ /// will have either the forward, reverse, or both domains populated.
+ ///
+ /// @param change_mask determines which change directions are requested
+ void setupForIPv6Transaction(dhcp_ddns::NameChangeType chg_type,
+ int change_mask);
+};
+
+
/// @brief Tests the number of RRs in a request section against a given count.
///
/// This function actually returns the number of RRsetPtrs in a section. Since
@@ -181,6 +239,31 @@ extern void checkForwardReplaceRequest(NameChangeTransaction& tran);
/// @param tran Transaction containing the request to be verified.
extern void checkReverseReplaceRequest(NameChangeTransaction& tran);
+/// @brief Verifies a forward address removal DNS update request
+///
+/// Tests that the DNS Update request for a given transaction, is correct for
+/// removing the forward address DNS entry.
+///
+/// @param tran Transaction containing the request to be verified.
+extern void checkForwardRemoveAddrsRequest(NameChangeTransaction& tran);
+
+/// @brief Verifies a forward RR removal DNS update request
+///
+/// Tests that the DNS Update request for a given transaction, is correct for
+/// removing forward RR DNS entries.
+///
+/// @param tran Transaction containing the request to be verified.
+extern void checkForwardRemoveRRsRequest(NameChangeTransaction& tran);
+
+/// @brief Verifies a reverse mapping removal DNS update request
+///
+/// Tests that the DNS Update request for a given transaction, is correct for
+/// removing a reverse DNS mapping.
+///
+/// @param tran Transaction containing the request to be verified.
+extern void checkReverseRemoveRequest(NameChangeTransaction& tran);
+
+
/// @brief Creates a NameChangeRequest from JSON string.
///
/// @param ncr_str string of JSON text from which to make the request.
More information about the bind10-changes
mailing list