-Kea 1.8.2 (stable) released on Dec 16, 2020
-
-1803. [build] razvan
- Library version numbers bumped for Kea 1.8.2 stable release
- version.
- (Gitlab #1583)
-
-1802. [bug] andrei, razvan
- Fix trivial logic error in handling the "lease4-update" command
- manifesting itself on v4 if multi-threading is enabled.
- Notable affected environment is a HA with the
- "send-lease-updates" configuration setting explicitly set to
- true. Prior to this fix, lease updates would not go through to
- other HA nodes, even though some log lines would say they would.
- A simple workaround prior to this fix is to disable
- multi-threading.
- (Gitlab #1542,#1572)
-
-Kea 1.8.1 (stable) released on Nov 12, 2020
-
-1801. [build] razvan
- Library version numbers bumped for Kea 1.8.1 stable release
- version.
- (Gitlab #1530)
-
-1800. [bug] razvan
+1824. [func] tmark
+ Added a new parameter, ddns-use-conflict-resolution, to
+ kea-dhcp4 and kea-dhcp6. This parameter is passed per request
+ to kea-dhcp-ddns which uses it to determine whether or not
+ conflict resolution rules (see RFC 4703) are followed for that
+ request. The default value is true. Disabling conflict
+ resolution should only be used after careful consideration.
+ (Gitlab #1386)
+
+1823. [doc] tomek
+ Updated options documentation for DHCPv4 and DHCPv6.
+ (Gitlab #1436, #1460)
+
+1822. [func] fdupont
+ When multi-threading is enabled the status-get command displays
+ the average lenght of the multi-threading packet queue for last
+ 10, 100 and 1000 packets.
+ (Gitlab #1306)
+
+1821. [func] anonymous, fdupont
+ The forensic log hook library now logs release and decline
+ events.
+ (Gitlab #1445)
+
+1820. [bug] razvan
Fixed lease update when using HA and lease_cmds hooks with
database backend. Previously, HA updates were rejected because
the database backend rejects operations on the lease if the old
expiration time is different than what it is already stored, to
act as a protection mechanism for parallel updates from several
threads or processes.
- (Gitlab #1434,#1488)
-
-1799. [bug] tmark
+ (Gitlab #1434)
+
+1819. [func] fdupont
+ Improved error messages for bad escapes in JSON strings.
+ (Gitlab #151)
+
+1818. [doc] andrei
+ Add to the reservation documentation:
+ * instructions on how to choose "reservation-mode"
+ * priority of "reservation-mode" specified at all levels
+ * priority of file reservations vs database reservations
+ (Gitlab #1299)
+
+1817. [func] fdupont
+ Redact control agent logs to hide basic HTTP authentication
+ passwords from the configuration files. Note that when HTTP
+ headers are logged credentials are present in clear text.
+ (Gitlab #1459)
+
+1816. [func] fdupont
+ The message logged when basic HTTP authentication succeed is
+ now informative (was DEBUG, is INFO now).
+ (Gitlab #1450)
+
+1815. [bug] marcin
+ Fixed libdhcpsrv build failures when building without database
+ backends.
+ (Gitlab #1468)
+
+1814. [func] marcin
+ Added ip-reservations-unique global parameter which controls
+ whether or not it is allowed to create multiple host reservations
+ for the same IP address or delegated prefix. By default, it is
+ not allowed to create multiple reservations for the same lease
+ within the same subnet. This change facilitates the use case
+ in which a single host can communicate with the DHCP server over
+ multiple network interfaces but should be assigned the same
+ reserved lease regardless of which interface is used.
+ (Gitlab #1428)
+
+1813. [func] tmark
+ A new parameter, ddns-update-on-renew, has been added to
+ kea-dhcp4 and kea-dhcp6 configuration. When true, the server
+ will always update DNS when a lease is renewed even if the DNS
+ information for the lease has not changed. The prior, and now
+ default, behavior is for the server to only update DNS for a
+ renewing lease if its DNS information has changed.
+ (Gitlab #1385)
+
+1812. [doc] andrei
+ Document how MAC addresses can be formatted for use as attributes
+ in RADIUS authentication
+ (Gitlab #1441)
+
+1811. [func] fdupont
+ Two new parameters were added: cache-threshold and cache-max-age
+ to the DHCPv4 and DHCPv6 global scopes. They will govern the
+ upcoming cache thresfold feature. The parameters can be set and
+ retrieved, but they're not used yet.
+ (Gitlab #1418)
+
+Kea 1.9.0 (development) released on Sep 30, 2020
+
+1810. [build] fdupont, razvan
+ Bump up libs version for Kea 1.9.0 release.
+ (Gitlab #1400)
+
+1809. [func] razvan
+ Added csv-format option to flex_option hook to be able to insert
+ option data in csv format. The implicit value is false,
+ maintaining compatibility with the previous default raw format.
+ (Gitlab #1373)
+
+1808. [func] razvan
+ Support for new IPv6-only-preferred option for DHCPv4 has been
+ added. It lets Kea to signal to compatible devices that the
+ IPv6 connectivity is available and they can disable their
+ IPv4 stack. This implements support for draft-ietf-dhc-v6only-08,
+ which is expected to be soon published by IETF as an RFC.
+ (Gitlab #1351)
+
+1807. [doc] tomek
+ Added separate table with DHCPv4 options that are governed by Kea
+ itself, rather than configured by administrator.
+ (Gitlab #1323, #1398)
+
+1806. [bug] tmark
+ The DNS update code behaving better when there is a shared
+ network and the code initially selected one subnet, but then
+ later determined that a different subnet will be used. There
+ is still a corner-case in DHCPv6 if the client requests multiple
+ addresses or multiple prefixes and some of them is serviced from
+ one subnet and some from another.
+ (Gitlab #1389)
+
+1805. [doc] tomek
+ API documentation for lease4-get-* and lease6-get-* commands
+ has been updated.
+ (Gitlab #1392)
+
+1804. [func] fdupont
+ Added a new reservation-get-by-id command to retrieve all host
+ reservations with an identifier value and type. Made the
+ subnet-id optional in the reservation-get-page command.
+ (Gitlab #1163)
+
+1803. [doc] fdupont
+ Corrected Kea ARM sections describing how to send DHCPv6
+ Vendor-specific Information Option (code 17) with sub-options.
+ (Gitlab #1025)
+
+1802. [bug] fdupont
+ Removed the bug which allowed for repeating the same
+ configuration parameter multiple times in the same scope.
+ The second occurrence of the parameter overwrote the first
+ occurrence causing server misconfiguration. Starting from
+ this change an error is raised when the same parameter occurs
+ multiple times in a given scope the location of the first value.
+ (Gitlab #1102)
+
+1801. [doc] fdupont
+ Moved JSON files describing commands to the share directory
+ and adding a new access entry taking read or write values.
+ (Gitlab #1240)
+
+1800. [func] fdupont
+ Added support of basic HTTP authentication in HTTP library,
+ control agent, kea shell and high availability hook.
+ (Gitlab #1304)
+
+1799. [bug] fdupont
+ Checked execution of queries to get schema versions of MySQL
+ and PostgreSQL database in kea-admin.
+ (Gitlab #828)
+
+1798. [bug] tmark
kea-dhcp4 now correctly updates DNS when a client
returns for lease after the lease has expired. Prior
to this, the server would remove the entries but then
fail to add them unless the hostname (or FQDN) changed.
This change also eliminates redundant DNS removes when
expired leases are reclaimed and given to different clients.
- (Gitlab #1419,#1409)
-
-1798. [func] razvan,tmark
- Removed "Multithreading is experimental" warning log message
- from kea-dhcp4 and kea-dhcp6 servers.
- (Gitlab #1435,#1431)
+ (Gitlab #1409)
Kea 1.8.0 (stable) released on Aug 26, 2020
Kea HTTP client re-establishes connection with the HTTP server
when timeout occurs. Prior to this change the client tried to
reuse the connection after timeout often getting subsequent
- timeouts. Re-establishing the connection may help avoid further
+ timeouts. Re-establishing the connection may help avoid further
timeouts during the High Availability operation.
(Gitlab #1390)
(Gitlab #505)
1794. [func] razvan
- The 'status-get' command send to DHCPv4 or DHCPv6 servers will
- return information about multi threading settings
- ('multi-threading-enabled', and only if this setting is active,
- 'thread-pool-size' and 'packet-queue-size').
+ The 'status-get' command send to DHCPv4 or DHCPv6 servers will return
+ information about multi threading settings ('multi-threading-enabled',
+ and only if this setting is active, 'thread-pool-size' and
+ 'packet-queue-size').
(Gitlab #1305)
1793. [doc] tmark
(Gitlab #1384)
1790. [bug] marcin
- Fixed a bug in Kea Configuration Backend observed when using
- certain MariaDB database versions. As a result of this bug the
- Kea servers were unable to fetch configurations stored in the
- database upon startup. The bug was related to the usage of
- timestamps which in MariaDB must be set to 1970-01-01 00:00:01
- UTC or later. In some cases the Kea CB used out of bounds
- timestamp values. This bug was observed when using MariaDB
+ Fixed a bug in Kea Configuration Backend observed when using certain
+ MariaDB database versions. As a result of this bug the Kea servers were
+ unable to fetch configurations stored in the database upon startup. The
+ bug was related to the usage of timestamps which in MariaDB must be
+ set to 1970-01-01 00:00:01 UTC or later. In some cases the Kea CB used
+ out of bounds timestamp values. This bug was observed when using MariaDB
10.4.13.
(Gitlab #1382)
1789. [bug] razvan
- Fixed a bug in the lease reclaim process which would not
- reschedule the timer if the operation failed.
+ Fixed a bug in the lease reclaim process which would not reschedule the
+ timer if the operation failed.
(Gitlab #1335)
1788. [bug]* fdupont
(Gitlab #1254)
1787. [bug] razvan
- The recount leases functions consider leases in 'declined'
- state as 'assigned' so that when the lease is reclaimed or
- reused, no negative counters are generated. The
- 'subnet[X].reclaimed-leases' and 'reclaimed-leases' are now
- cumulative counters and are never decremented or reset until
- server restart. Removed references to non existent
- 'declined-reclaimed-addresses' counters.
+ The recount leases functions consider leases in 'declined' state as
+ 'assigned' so that when the lease is reclaimed or reused, no negative
+ counters are generated. The 'subnet[X].reclaimed-leases' and
+ 'reclaimed-leases' are now cumulative counters and are never
+ decremented or reset until server restart. Removed references to non
+ existent 'declined-reclaimed-addresses' counters.
(Gitlab #1336)
1786. [bug] razvan
(Gitlab #1065)
1785. [func] fdupont
- If enabled, the default value for queue capacity in congestion
- control has been trimmed down from 500 to 64. The feature
- continues to be disabled by default.
+ If enabled, the default value for queue capacity in congestion control
+ has been trimmed down from 500 to 64. The feature continues to be disabled
+ by default.
(Gitlab #285)
1784. [build] wlodek
(Gitlab #1310)
1782. [bug] tmark
- Corrected a bug that causes kea-dhcp4/kea-dhcp6 servers to
- crash after losing connectivity to a configuration backend
- database. The servers now correctly honor the
- max-reconnect-tries parameter.
+ Corrected a bug that causes kea-dhcp4/kea-dhcp6 servers to crash after
+ losing connectivity to a configuration backend database. The servers
+ now correctly honor the max-reconnect-tries parameter.
(Gitlab #1369)
1781. [bug] razvan
// to kea-dhcp-ddns.
"ddns-send-updates": true,
+ // Boolean flag, which when true instructs the server to always
+ // update DNS when leases are renewed, even if the DNS information
+ // has not changed. The server's default behavior (i.e. flag is false)
+ // is to only update DNS if the DNS information has changed. It
+ // may be specified at the global, shared-network and subnet levels.
+ "ddns-update-on-renew": true,
+
+ // Boolean flag, which is passed to kea-dhcp-ddns with each DDNS
+ // update request to indicate whether or not DNS update conflict
+ // resolution as described in RFC 4703 should be employed for the
+ // given update request. The default value for this flag is true.
+ // It may be specified at the global, shared-network and subnet levels.
+ "ddns-use-conflict-resolution": true,
+
// Time in seconds specifying how long a declined lease should be
// excluded from DHCP assignments. The default value is 24 hours.
"decline-probation-period": 86400,
// Shared network level value. See description at the global level.
"ddns-send-updates": true,
+ // Shared network level value. See description at the global level.
+ "ddns-update-on-renew": true,
+
+ // Shared network level value. See description at the global level.
+ "ddns-use-conflict-resolution": true,
+
// Shared network level value. See description at the global level.
"hostname-char-replacement": "x",
// Subnet level value. See description at the global level.
"ddns-send-updates": true,
+ // Subnet level value. See description at the global level.
+ "ddns-update-on-renew": true,
+
+ // Subnet level value. See description at the global level.
+ "ddns-use-conflict-resolution": true,
+
// Subnet level value. See description at the global level.
"hostname-char-replacement": "x",
"ddns-replace-client-name" : "when-present",
"ddns-generated-prefix" : "test.prefix",
"ddns-qualifying-suffix" : "test.suffix.",
+ "ddns-update-on-renew" : false,
+ "ddns-use-conflict-resolution" : true,
"hostname-char-set": "[^A-Za-z0-9.-]",
"hostname-char-replacement": "x",
// to kea-dhcp-ddns.
"ddns-send-updates": true,
+ // Boolean flag, which when true instructs the server to always
+ // update DNS when leases are renewed, even if the DNS information
+ // has not changed. The server's default behavior (i.e. flag is false)
+ // is to only update DNS if the DNS information has changed. It
+ // may be specified at the global, shared-network and subnet levels.
+ "ddns-update-on-renew": true,
+
+ // Boolean flag, which is passed to kea-dhcp-ddns with each DDNS
+ // update request to indicate whether or not DNS update conflict
+ // resolution as described in RFC 4703 should be employed for the
+ // given update request. The default value for this flag is true.
+ // It may be specified at the global, shared-network and subnet levels.
+ "ddns-use-conflict-resolution": true,
+
// Time in seconds specifying how long a declined lease should be
// excluded from DHCP assignments. The default value is 24 hours.
"decline-probation-period": 86400,
// Shared network level value. See description at the global level.
"ddns-send-updates": true,
+ // Shared network level value. See description at the global level.
+ "ddns-update-on-renew": true,
+
+ // Shared network level value. See description at the global level.
+ "ddns-use-conflict-resolution": true,
+
// Shared network level value. See description at the global level.
"hostname-char-replacement": "x",
// Subnet level value. See description at the global level.
"ddns-send-updates": true,
+ // Subnet level value. See description at the global level.
+ "ddns-update-on-renew": true,
+
+ // Subnet level value. See description at the global level.
+ "ddns-use-conflict-resolution": true,
+
// Subnet level value. See description at the global level.
"hostname-char-replacement": "x",
"ddns-replace-client-name" : "when-present",
"ddns-generated-prefix" : "test.prefix",
"ddns-qualifying-suffix" : "test.suffix.",
+ "ddns-update-on-renew" : false,
+ "ddns-use-conflict-resolution" : true,
"hostname-char-set": "[^A-Za-z0-9.-]",
"hostname-char-replacement": "x",
- ``ddns-replace-client-name"``
- ``ddns-generated-prefix``
- ``ddns-qualifying-suffix``
+ - ``ddns-update-on-renew``
+ - ``ddns-use-conflict-resolution``
- ``hostname-char-set``
- ``hostname-char-replacement``
"ddns-replace-client-name": "never",
"ddns-generated-prefix": "myhost",
"ddns-qualifying-suffix": "",
+ "ddns-update-on-renew": false,
+ "ddns-use-conflict-resolution": true,
"hostname-char-set": "",
"hostname-char-replacement": ""
...
| | | false for ddns-enable-updates |
+-----------------+--------------------+-------------------------------+
+Kea 1.9.1 adds two new parameters. The first new parameter is ``ddns-update-on-renew``.
+Normally, when leases are renewed the server will only update DNS if the DNS
+information for the lease (e.g. FQDN, DNS update direction flags) has changed.
+Setting ``ddns-update-on-renew`` to true instructs the server to always update
+the DNS information when a lease is renewed even if its DNS information has not
+changed. This allows Kea to "self-heal" in the event it was previously unable
+to add DNS entries or they were somehow lost by the DNS server.
+
+.. note::
+
+ Setting ``ddns-update-on-renew`` to true may impact performance, especially
+ for servers with numerous clients who renew often.
+
+The second parameter added in Kea 1.9.1 is ``ddns-use-conflict-resolution``.
+The value of this parameter is passed by kea-dhcp4 to D2 with each DNS update
+request. When true, (the default value), D2 will employ conflict resolution,
+as described in `RFC 4703 <https://tools.ietf.org/html/rfc4703>`__, when
+attempting to fulfill the update request. When false, D2 will simply attempt
+to update the DNS entries per the request, regardless of whether or not they
+conflict with existing entries owned by other DHCP4 clients.
+
+.. note::
+
+ Setting ``ddns-use-conflict-resolution`` to false disables the overwrite
+ safeguards that the rules of conflict resolution (
+ `RFC 4703 <https://tools.ietf.org/html/rfc4703>`__) are intended to
+ prevent. This means that existing entries for a FQDN or an
+ IP address made for Client-A can be deleted or replaced by entries
+ for Client-B. Furthermore, there are two scenarios by which entries
+ for multiple clients for the same key (e.g. FQDN or IP) can be created.
+
+ 1. Client-B uses the same FQDN as Client-A but a different IP address.
+ In this case the forward DNS entries (A and DHCID RRs) for
+ Client-A will be deleted as they match the FQDN and new entries for
+ Client-B will be added. The reverse DNS entries (PTR and DHCID RRs)
+ for Client-A, however, will not be deleted as they belong to a different
+ IP address while new entries for Client-B will still be added.
+
+ 2. Client-B uses the same IP address as Client-A but a different FQDN.
+ In this case the reverse DNS entries (PTR and DHCID RRs) for Client-A
+ will be deleted as they match the IP address and new entries for
+ Client-B will be added. The forward DNS entries (A and DHCID RRs)
+ for Client-A, however, will not be deleted as they belong to a different
+ FQDN while new entries for Client-B will still be added.
+
+ Disabling conflict resolution should be done only after careful review of
+ your specific use cases. The best way to avoid unwanted DNS entries is to
+ always ensure leases changes are processed through Kea, whether they are
+ released, expire, or are deleted via the lease-del4 command, prior to
+ reassigning either FQDNs or IP addresses. Doing so will cause kea-dhcp4
+ to generate DNS removal requests to D2.
+
.. _dhcpv4-d2-io-config:
DHCP-DDNS Server Connectivity
- ``ddns-replace-client-name"``
- ``ddns-generated-prefix``
- ``ddns-qualifying-suffix``
+ - ``ddns-update-on-renew``
+ - ``ddns-use-conflict-resolution``
- ``hostname-char-set``
- ``hostname-char-replacement``
"ddns-replace-client-name": "never",
"ddns-generated-prefix": "myhost",
"ddns-qualifying-suffix": "",
+ "ddns-update-on-renew": false,
+ "ddns-use-conflict-resolution": true,
"hostname-char-set": "",
"hostname-char-replacement": ""
...
| | | false for ddns-enable-updates |
+-----------------+--------------------+-------------------------------+
+
+Kea 1.9.1 adds two new parameters. The first new parameter is ``ddns-update-on-renew``.
+Normally, when leases are renewed the server will only update DNS if the DNS
+information for the lease (e.g. FQDN, DNS update direction flags) has changed.
+Setting ``ddns-update-on-renew`` to true instructs the server to always update
+the DNS information when a lease is renewed even if its DNS information has not
+changed. This allows Kea to "self-heal" in the event it was previously unable
+to add DNS entries or they were somehow lost by the DNS server.
+
+.. note::
+
+ Setting ``ddns-update-on-renew`` to true may impact performance, especially
+ for servers with numerous clients who renew often.
+
+The second parameter added in Kea 1.9.1 is ``ddns-use-conflict-resolution``.
+The value of this parameter is passed by kea-dhcp6 to D2 with each DNS update
+request. When true, (the default value), D2 will employ conflict resolution,
+as described in `RFC 4703 <https://tools.ietf.org/html/rfc4703>`__, when
+attempting to fulfill the update request. When false, D2 will simply attempt
+to update the DNS entries per the request, regardless of whether or not they
+conflict with existing entries owned by other DHCP6 clients.
+
+.. note::
+
+ Setting ``ddns-use-conflict-resolution`` to false disables the overwrite
+ safeguards that the rules of conflict resolution (
+ `RFC 4703 <https://tools.ietf.org/html/rfc4703>`__) are intended to
+ prevent. This means that existing entries for a FQDN or an
+ IP address made for Client-A can be deleted or replaced by entries
+ for Client-B. Furthermore, there are two scenarios by which entries
+ for multiple clients for the same key (e.g. FQDN or IP) can be created.
+
+ 1. Client-B uses the same FQDN as Client-A but a different IP address.
+ In this case the forward DNS entries (AAAA, and DHCID RRs) for
+ Client-A will be deleted as they match the FQDN and new entries for
+ Client-B will be added. The reverse DNS entries (PTR and DHCID RRs)
+ for Client-A, however, will not be deleted as they belong to a different
+ IP address while new entries for Client-B will still be added.
+
+ 2. Client-B uses the same IP address as Client-A but a different FQDN.
+ In this case the reverse DNS entries (PTR and DHCID RRs) for Client-A
+ will be deleted as they match the IP address and new entries for
+ Client-B will be added. The forward DNS entries (AAAA and DHCID RRs)
+ for Client-A, however, will not be deleted as they belong to a different
+ FQDN while new entries for Client-B will still be added.
+
+ Disabling conflict resolution should be done only after careful review of
+ your specific use cases. The best way to avoid unwanted DNS entries is to
+ always ensure leases changes are processed through Kea, whether they are
+ released, expire, or are deleted via the lease-del6 command, prior to
+ reassigning either FQDNs or IP addresses. Doing so will cause kea-dhcp6
+ to generate DNS removal requests to D2.
+
+
.. _dhcpv6-d2-io-config:
DHCP-DDNS Server Connectivity
libd2_la_SOURCES += d2_controller.cc d2_controller.h
libd2_la_SOURCES += parser_context.cc parser_context.h parser_context_decl.h
libd2_la_SOURCES += d2_messages.h d2_messages.cc
+libd2_la_SOURCES += simple_add.cc simple_add.h
+libd2_la_SOURCES += simple_remove.cc simple_remove.h
EXTRA_DIST += d2_messages.mes
sbin_PROGRAMS = kea-dhcp-ddns
-// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
#include <d2/d2_update_mgr.h>
#include <d2/nc_add.h>
#include <d2/nc_remove.h>
+#include <d2/simple_add.h>
+#include <d2/simple_remove.h>
#include <sstream>
#include <iostream>
// the transaction's IO.
NameChangeTransactionPtr trans;
if (next_ncr->getChangeType() == dhcp_ddns::CHG_ADD) {
- trans.reset(new NameAddTransaction(io_service_, next_ncr,
- forward_domain, reverse_domain,
- cfg_mgr_));
+ if (next_ncr->useConflictResolution()) {
+ trans.reset(new NameAddTransaction(io_service_, next_ncr,
+ forward_domain, reverse_domain,
+ cfg_mgr_));
+ } else {
+ trans.reset(new SimpleAddTransaction(io_service_, next_ncr,
+ forward_domain, reverse_domain,
+ cfg_mgr_));
+ }
} else {
- trans.reset(new NameRemoveTransaction(io_service_, next_ncr,
- forward_domain, reverse_domain,
- cfg_mgr_));
+ if (next_ncr->useConflictResolution()) {
+ trans.reset(new NameRemoveTransaction(io_service_, next_ncr,
+ forward_domain, reverse_domain,
+ cfg_mgr_));
+ } else {
+ trans.reset(new SimpleRemoveTransaction(io_service_, next_ncr,
+ forward_domain, reverse_domain,
+ cfg_mgr_));
+ }
}
// Add the new transaction to the list.
-// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
/// of servers is an error which will be logged and the request will be
/// discarded.
///
+ /// Finally, If conflict resolution is enabled, it will instantiate either
+ /// a NameAddTransaction or a NameRemoveTransaction. If disabled it will
+ /// instantiate either a SimpleAddTransaction or a SimpleRemoveTransaction.
+ ///
/// @param ncr the NameChangeRequest for which to create a transaction.
///
/// @throw D2UpdateMgrError if a transaction for this DHCID already
--- /dev/null
+// Copyright (C) 2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <d2/d2_log.h>
+#include <d2/d2_cfg_mgr.h>
+#include <d2/simple_add.h>
+
+#include <util/buffer.h>
+#include <dns/rdataclass.h>
+
+#include <functional>
+
+namespace isc {
+namespace d2 {
+
+// SimpleAddTransaction states
+const int SimpleAddTransaction::REPLACING_FWD_ADDRS_ST;
+const int SimpleAddTransaction::REPLACING_REV_PTRS_ST;
+
+// SimpleAddTransaction events
+const int SimpleAddTransaction::FQDN_IN_USE_EVT;
+const int SimpleAddTransaction::FQDN_NOT_IN_USE_EVT;
+
+SimpleAddTransaction::
+SimpleAddTransaction(asiolink::IOServicePtr& io_service,
+ dhcp_ddns::NameChangeRequestPtr& ncr,
+ DdnsDomainPtr& forward_domain,
+ DdnsDomainPtr& reverse_domain,
+ D2CfgMgrPtr& cfg_mgr)
+ : NameChangeTransaction(io_service, ncr, forward_domain, reverse_domain,
+ cfg_mgr) {
+ if (ncr->getChangeType() != isc::dhcp_ddns::CHG_ADD) {
+ isc_throw (SimpleAddTransactionError,
+ "SimpleAddTransaction, request type must be CHG_ADD");
+ }
+}
+
+SimpleAddTransaction::~SimpleAddTransaction(){
+}
+
+void
+SimpleAddTransaction::defineEvents() {
+ // Call superclass impl first.
+ NameChangeTransaction::defineEvents();
+
+ // Define SimpleAddTransaction events.
+ defineEvent(FQDN_IN_USE_EVT, "FQDN_IN_USE_EVT");
+ defineEvent(FQDN_NOT_IN_USE_EVT, "FQDN_NOT_IN_USE_EVT");
+}
+
+void
+SimpleAddTransaction::verifyEvents() {
+ // Call superclass implementation first to verify its events. These are
+ // events common to all transactions, and they must be defined.
+ // SELECT_SERVER_EVT
+ // SERVER_SELECTED_EVT
+ // SERVER_IO_ERROR_EVT
+ // NO_MORE_SERVERS_EVT
+ // IO_COMPLETED_EVT
+ // UPDATE_OK_EVT
+ // UPDATE_FAILED_EVT
+ NameChangeTransaction::verifyEvents();
+
+ // Verify SimpleAddTransaction events by attempting to fetch them.
+ getEvent(FQDN_IN_USE_EVT);
+ getEvent(FQDN_NOT_IN_USE_EVT);
+}
+
+void
+SimpleAddTransaction::defineStates() {
+ // Call superclass impl first.
+ NameChangeTransaction::defineStates();
+
+ // Define SimpleAddTransaction states.
+ defineState(READY_ST, "READY_ST",
+ std::bind(&SimpleAddTransaction::readyHandler, this));
+
+ defineState(SELECTING_FWD_SERVER_ST, "SELECTING_FWD_SERVER_ST",
+ std::bind(&SimpleAddTransaction::selectingFwdServerHandler, this));
+
+ defineState(SELECTING_REV_SERVER_ST, "SELECTING_REV_SERVER_ST",
+ std::bind(&SimpleAddTransaction::selectingRevServerHandler, this));
+
+ defineState(REPLACING_FWD_ADDRS_ST, "REPLACING_FWD_ADDRS_ST",
+ std::bind(&SimpleAddTransaction::replacingFwdAddrsHandler, this));
+
+ defineState(REPLACING_REV_PTRS_ST, "REPLACING_REV_PTRS_ST",
+ std::bind(&SimpleAddTransaction::replacingRevPtrsHandler, this));
+
+ defineState(PROCESS_TRANS_OK_ST, "PROCESS_TRANS_OK_ST",
+ std::bind(&SimpleAddTransaction::processAddOkHandler, this));
+
+ defineState(PROCESS_TRANS_FAILED_ST, "PROCESS_TRANS_FAILED_ST",
+ std::bind(&SimpleAddTransaction::processAddFailedHandler, this));
+}
+
+void
+SimpleAddTransaction::verifyStates() {
+ // Call superclass implementation first to verify its states. These are
+ // states common to all transactions, and they must be defined.
+ // READY_ST
+ // SELECTING_FWD_SERVER_ST
+ // SELECTING_REV_SERVER_ST
+ // PROCESS_TRANS_OK_ST
+ // PROCESS_TRANS_FAILED_ST
+ NameChangeTransaction::verifyStates();
+
+ // Verify SimpleAddTransaction states by attempting to fetch them.
+ getStateInternal(REPLACING_FWD_ADDRS_ST);
+ getStateInternal(REPLACING_REV_PTRS_ST);
+}
+
+void
+SimpleAddTransaction::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(SimpleAddTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+}
+
+void
+SimpleAddTransaction::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(SimpleAddTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+
+ // Select the next server from the list of forward servers.
+ if (selectNextServer()) {
+ // We have a server to try.
+ transition(REPLACING_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
+SimpleAddTransaction::replacingFwdAddrsHandler() {
+ 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 {
+ buildReplaceFwdAddressRequest();
+ } 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(d2_to_dns_logger, DHCP_DDNS_FORWARD_ADD_BUILD_FAILURE)
+ .arg(getRequestId())
+ .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("Forward Add");
+ 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()) {
+ // We were able to add it. 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 {
+ // Any other value means cease. Really shouldn't happen.
+ LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_FORWARD_ADD_REJECTED)
+ .arg(getRequestId())
+ .arg(getCurrentServer()->toText())
+ .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(d2_to_dns_logger, DHCP_DDNS_FORWARD_ADD_IO_ERROR)
+ .arg(getRequestId())
+ .arg(getNcr()->getFqdn())
+ .arg(getCurrentServer()->toText());
+
+ 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(d2_to_dns_logger, DHCP_DDNS_FORWARD_ADD_RESP_CORRUPT)
+ .arg(getRequestId())
+ .arg(getCurrentServer()->toText())
+ .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(d2_to_dns_logger, DHCP_DDNS_FORWARD_ADD_BAD_DNSCLIENT_STATUS)
+ .arg(getRequestId())
+ .arg(getDnsUpdateStatus())
+ .arg(getNcr()->getFqdn())
+ .arg(getCurrentServer()->toText());
+
+ 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(SimpleAddTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+}
+
+void
+SimpleAddTransaction::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(SimpleAddTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+
+ // Select the next server from the list of forward servers.
+ if (selectNextServer()) {
+ // We have a server to try.
+ transition(REPLACING_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
+SimpleAddTransaction::replacingRevPtrsHandler() {
+ 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 {
+ buildReplaceRevPtrsRequest();
+ } 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(d2_to_dns_logger, DHCP_DDNS_REVERSE_REPLACE_BUILD_FAILURE)
+ .arg(getRequestId())
+ .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("Reverse Replace");
+ 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()) {
+ // We were able to update the reverse mapping. Mark it as done.
+ 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(d2_to_dns_logger, DHCP_DDNS_REVERSE_REPLACE_REJECTED)
+ .arg(getRequestId())
+ .arg(getCurrentServer()->toText())
+ .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(d2_to_dns_logger, DHCP_DDNS_REVERSE_REPLACE_IO_ERROR)
+ .arg(getRequestId())
+ .arg(getNcr()->getFqdn())
+ .arg(getCurrentServer()->toText());
+
+ // 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(d2_to_dns_logger, DHCP_DDNS_REVERSE_REPLACE_RESP_CORRUPT)
+ .arg(getRequestId())
+ .arg(getCurrentServer()->toText())
+ .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(d2_to_dns_logger,
+ DHCP_DDNS_REVERSE_REPLACE_BAD_DNSCLIENT_STATUS)
+ .arg(getRequestId())
+ .arg(getDnsUpdateStatus())
+ .arg(getNcr()->getFqdn())
+ .arg(getCurrentServer()->toText());
+
+ 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(SimpleAddTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+}
+
+void
+SimpleAddTransaction::processAddOkHandler() {
+ switch(getNextEvent()) {
+ case UPDATE_OK_EVT:
+ LOG_INFO(d2_to_dns_logger, DHCP_DDNS_ADD_SUCCEEDED)
+ .arg(getRequestId())
+ .arg(getNcr()->toText());
+ setNcrStatus(dhcp_ddns::ST_COMPLETED);
+ endModel();
+ break;
+ default:
+ // Event is invalid.
+ isc_throw(SimpleAddTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+}
+
+void
+SimpleAddTransaction::processAddFailedHandler() {
+ switch(getNextEvent()) {
+ case UPDATE_FAILED_EVT:
+ case NO_MORE_SERVERS_EVT:
+ setNcrStatus(dhcp_ddns::ST_FAILED);
+ LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_ADD_FAILED)
+ .arg(getRequestId())
+ .arg(transactionOutcomeString());
+ endModel();
+ break;
+ default:
+ // Event is invalid.
+ isc_throw(SimpleAddTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+}
+
+void
+SimpleAddTransaction::buildReplaceFwdAddressRequest() {
+ // Construct an empty request.
+ D2UpdateMessagePtr request = prepNewRequest(getForwardDomain());
+
+ // Construct dns::Name from NCR fqdn.
+ dns::Name fqdn(dns::Name(getNcr()->getFqdn()));
+
+ // There are no prerequisites.
+
+ // Build the Update Section. First we delete any pre-existing
+ // FQDN/IP and DHCID RRs. Then we add new ones.
+
+ // Create the FQDN/IP 'delete' RR and add it 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 DHCID 'delete' RR and add it to the update section.
+ update.reset(new dns::RRset(fqdn, dns::RRClass::ANY(),
+ dns::RRType::DHCID(), dns::RRTTL(0)));
+ request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+
+ // Now make the new RRs.
+ // Create the TTL based on lease length.
+ dns::RRTTL lease_ttl(getNcr()->getLeaseLength());
+
+ // Create the FQDN/IP 'add' RR and add it to the to update section.
+ // Based on RFC 2136, section 2.5.1
+ update.reset(new dns::RRset(fqdn, dns::RRClass::IN(),
+ getAddressRRType(), lease_ttl));
+
+ addLeaseAddressRdata(update);
+ request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+
+ // Now create the FQDN/DHCID 'add' RR and add it to update section.
+ // Based on RFC 2136, section 2.5.1
+ update.reset(new dns::RRset(fqdn, dns::RRClass::IN(),
+ dns::RRType::DHCID(), lease_ttl));
+
+ // We add the DHCID for auditing purposes and in the event
+ // conflict resolution is later enabled.
+ addDhcidRdata(update);
+ request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+
+ // Set the transaction's update request to the new request.
+ setDnsUpdateRequest(request);
+}
+
+void
+SimpleAddTransaction::buildReplaceRevPtrsRequest() {
+ // Construct an empty request.
+ D2UpdateMessagePtr request = prepNewRequest(getReverseDomain());
+
+ // Create the reverse IP address "FQDN".
+ std::string rev_addr = D2CfgMgr::reverseIpAddress(getNcr()->getIpAddress());
+ dns::Name rev_ip(rev_addr);
+
+ // Create the TTL based on lease length.
+ dns::RRTTL lease_ttl(getNcr()->getLeaseLength());
+
+ // There are no prerequisites.
+
+ // Create the FQDN/IP PTR 'delete' RR for this IP and add it to
+ // the 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(), lease_ttl));
+ 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(), lease_ttl));
+ addDhcidRdata(update);
+ request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+
+ // Set the transaction's update request to the new request.
+ setDnsUpdateRequest(request);
+}
+
+} // namespace isc::d2
+} // namespace isc
--- /dev/null
+// Copyright (C) 2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef SIMPLE_ADD_H
+#define SIMPLE_ADD_H
+
+/// @file nc_add.h This file defines the class SimpleAddTransaction.
+
+#include <d2/nc_trans.h>
+#include <dns/rdata.h>
+
+namespace isc {
+namespace d2 {
+
+/// @brief Thrown if the SimpleAddTransaction encounters a general error.
+class SimpleAddTransactionError : public isc::Exception {
+public:
+ SimpleAddTransactionError(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 Add update.
+///
+/// SimpleAddTransaction implements a state machine for adding (or replacing) a
+/// forward and/or reverse DNS mapping. This state machine follows a basic
+/// remove and replace scheme, that does not attempt to avoid conflicts
+/// between updating clients. The logic may be paraphrased as follows:
+///
+/// @code
+///
+/// If the request includes a forward change:
+/// Select a forward server
+/// Send the server a request to delete and then add forward entry
+///
+/// If the request includes a reverse change:
+/// Select a reverse server
+/// Send a server a request to delete and then add reverse entry
+///
+/// @endcode
+///
+/// This class derives from NameChangeTransaction from which it inherits
+/// states, events, and methods common to NameChangeRequest processing.
+class SimpleAddTransaction : public NameChangeTransaction {
+public:
+
+ //@{ Additional states needed for SimpleAdd state model.
+ /// @brief State that attempts to add forward address records.
+ static const int REPLACING_FWD_ADDRS_ST = NCT_DERIVED_STATE_MIN + 1;
+
+ /// @brief State that attempts to replace reverse PTR records
+ static const int REPLACING_REV_PTRS_ST = NCT_DERIVED_STATE_MIN + 3;
+ //@}
+
+ //@{ Additional events needed for SimpleAdd state model.
+ /// @brief Event sent when an add attempt fails with address in use.
+ static const int FQDN_IN_USE_EVT = NCT_DERIVED_EVENT_MIN + 1;
+
+ /// @brief Event sent when replace attempt to fails with address not in use.
+ static const int FQDN_NOT_IN_USE_EVT = NCT_DERIVED_EVENT_MIN + 2;
+ //@}
+
+ /// @brief Constructor
+ ///
+ /// Instantiates an Add 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
+ /// @param cfg_mgr pointer to the configuration manager
+ ///
+ /// @throw SimpleAddTransaction error if given request is not a CHG_ADD,
+ /// NameChangeTransaction error for base class construction errors.
+ SimpleAddTransaction(asiolink::IOServicePtr& io_service,
+ dhcp_ddns::NameChangeRequestPtr& ncr,
+ DdnsDomainPtr& forward_domain,
+ DdnsDomainPtr& reverse_domain,
+ D2CfgMgrPtr& cfg_mgr);
+
+ /// @brief Destructor
+ virtual ~SimpleAddTransaction();
+
+protected:
+ /// @brief Adds events defined by SimpleAddTransaction to the event set.
+ ///
+ /// Invokes NameChangeTransaction's implementation and then defines the
+ /// events unique to NCR Add 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
+ /// Add transaction's. This tests that the needed events are in the event
+ /// dictionary.
+ ///
+ /// @throw StateModelError if an event value is undefined.
+ virtual void verifyEvents();
+
+ /// @brief Adds states defined by SimpleAddTransaction to the state set.
+ ///
+ /// Invokes NameChangeTransaction's implementation and then defines the
+ /// states unique to NCR Add 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
+ /// Add transaction's states. This tests that the needed states are in the
+ /// state dictionary.
+ ///
+ /// @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 SimpleAddTransactionError 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
+ /// - REPLACING_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:
+ /// - REPLACING_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 SimpleAddTransactionError 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
+ /// - REPLACING_FWD_ADDRS_ST with next event of SELECT_SERVER_EVT
+ /// - REPLACING_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:
+ /// - REPLACING_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 SimpleAddTransactionError if upon entry next event is not
+ /// SELECT_SERVER_EVT or SERVER_IO_ERROR_EVT.
+ void selectingRevServerHandler();
+
+ /// @brief State handler for REPLACING_FWD_ADDRS_ST.
+ ///
+ /// Entered from:
+ /// - SELECTING_FWD_SERVER with next event of SERVER_SELECTED_EVT
+ ///
+ /// Attempts to replace a forward 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 forward add 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_COMPLETED_EVT,
+ /// the DNS update status is checked and acted upon accordingly:
+ ///
+ /// Transitions to:
+ /// - SELECTING_REV_SERVER_ST with next event of SELECT_SERVER_EVT upon
+ /// successful addition and the request includes a reverse DNS update.
+ ///
+ /// - PROCESS_TRANS_OK_ST with next event of UPDATE_OK_EVT upon successful
+ /// addition and no reverse DNS update is required.
+ ///
+ /// - PROCESS_TRANS_FAILED_ST with 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 states with next event of SERVER_SELECTED_EVT_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 SimpleAddTransactionError if upon entry next event is not
+ /// SERVER_SELECTED_EVT or IO_COMPLETE_EVT.
+ void replacingFwdAddrsHandler();
+
+ /// @brief State handler for REPLACING_REV_PTRS_ST.
+ ///
+ /// Entered from:
+ /// - SELECTING_REV_SERVER_ST with a next event of SERVER_SELECTED_EVT
+ ///
+ /// Attempts to delete and then add 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 replacement
+ /// add 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_COMPLETED_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 replacement.
+ ///
+ /// - 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 SimpleAddTransactionError if upon entry next event is not:
+ /// SERVER_SELECTED_EVT or IO_COMPLETED_EVT
+ void replacingRevPtrsHandler();
+
+ /// @brief State handler for PROCESS_TRANS_OK_ST.
+ ///
+ /// Entered from:
+ /// - REPLACING_FWD_ADDRS_ST with a next event of UPDATE_OK_EVT
+ /// - REPLACING_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 SimpleAddTransactionError if upon entry next event is not:
+ /// UPDATE_OK_EVT
+ void processAddOkHandler();
+
+ /// @brief State handler for PROCESS_TRANS_FAILED_ST.
+ ///
+ /// Entered from:
+ /// - SELECTING_FWD_SERVER_ST with a next event of NO_MORE_SERVERS
+ /// - 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
+ /// model execution.
+ ///
+ /// Transitions to:
+ /// - END_ST with a next event of FAIL_EVT.
+ ///
+ /// @throw SimpleAddTransactionError if upon entry next event is not:
+ /// UPDATE_FAILED_EVT
+ void processAddFailedHandler();
+
+ /// @brief Builds a DNS request to add/replace a forward DNS entry for an
+ /// FQDN
+ ///
+ /// Constructs a DNS update request, based upon the NCR, for adding a
+ /// forward DNS mapping. Once constructed, the request is stored as
+ /// the transaction's DNS update request.
+ ///
+ /// Prerequisite RRsets:
+ /// - There are no prerequisites.
+ ///
+ /// Updates RRsets:
+ /// -# A delete of any existing PTR RRs for the lease address
+ /// -# A delete of any existing DHCID RRs for the lease address
+ /// -# An FQDN/IP RR addition (type A for IPv4, AAAA for IPv6)
+ /// -# An FQDN/DHCID RR addition (type DHCID)
+ ///
+ /// @throw This method does not throw but underlying methods may.
+ void buildReplaceFwdAddressRequest();
+
+ /// @brief Builds a DNS request to replace a reverse DNS entry for an FQDN
+ ///
+ /// Constructs a DNS update request, based upon the NCR, for replacing a
+ /// reverse DNS mapping. Once constructed, the request is stored as
+ /// the transaction's DNS update request.
+ ///
+ /// Prerequisite RRsets:
+ /// - There are no prerequisites.
+ ///
+ /// Updates RRsets:
+ /// -# A delete of any existing PTR RRs for the lease address
+ /// -# A delete of any existing DHCID RRs for the lease address
+ /// -# A PTR RR addition for the lease address and FQDN
+ /// -# A DHCID RR addition for the lease address and lease client DHCID
+ ///
+ /// @throw This method does not throw but underlying methods may.
+ void buildReplaceRevPtrsRequest();
+};
+
+/// @brief Defines a pointer to a SimpleAddTransaction.
+typedef boost::shared_ptr<SimpleAddTransaction> SimpleAddTransactionPtr;
+
+} // namespace isc::d2
+} // namespace isc
+#endif
--- /dev/null
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <d2/d2_log.h>
+#include <d2/d2_cfg_mgr.h>
+#include <d2/simple_remove.h>
+
+#include <functional>
+
+namespace isc {
+namespace d2 {
+
+
+// SimpleRemoveTransaction states
+const int SimpleRemoveTransaction::REMOVING_FWD_RRS_ST;
+const int SimpleRemoveTransaction::REMOVING_REV_PTRS_ST;
+
+// SimpleRemoveTransaction events
+// Currently SimpleRemoveTransaction does not define any events.
+
+SimpleRemoveTransaction::
+SimpleRemoveTransaction(asiolink::IOServicePtr& io_service,
+ dhcp_ddns::NameChangeRequestPtr& ncr,
+ DdnsDomainPtr& forward_domain,
+ DdnsDomainPtr& reverse_domain,
+ D2CfgMgrPtr& cfg_mgr)
+ : NameChangeTransaction(io_service, ncr, forward_domain, reverse_domain,
+ cfg_mgr) {
+ if (ncr->getChangeType() != isc::dhcp_ddns::CHG_REMOVE) {
+ isc_throw (SimpleRemoveTransactionError,
+ "SimpleRemoveTransaction, request type must be CHG_REMOVE");
+ }
+}
+
+SimpleRemoveTransaction::~SimpleRemoveTransaction(){
+}
+
+void
+SimpleRemoveTransaction::defineEvents() {
+ // Call superclass impl first.
+ NameChangeTransaction::defineEvents();
+
+ // Define SimpleRemoveTransaction events.
+ // Currently SimpleRemoveTransaction does not define any events.
+ // defineEvent(TBD_EVENT, "TBD_EVT");
+}
+
+void
+SimpleRemoveTransaction::verifyEvents() {
+ // Call superclass implementation first to verify its events. These are
+ // events common to all transactions, and they must be defined.
+ // SELECT_SERVER_EVT
+ // SERVER_SELECTED_EVT
+ // SERVER_IO_ERROR_EVT
+ // NO_MORE_SERVERS_EVT
+ // IO_COMPLETED_EVT
+ // UPDATE_OK_EVT
+ // UPDATE_FAILED_EVT
+ NameChangeTransaction::verifyEvents();
+
+ // Verify SimpleRemoveTransaction events by attempting to fetch them.
+ // Currently SimpleRemoveTransaction does not define any events.
+ // getEvent(TBD_EVENT);
+}
+
+void
+SimpleRemoveTransaction::defineStates() {
+ // Call superclass impl first.
+ NameChangeTransaction::defineStates();
+
+ // Define SimpleRemoveTransaction states.
+ defineState(READY_ST, "READY_ST",
+ std::bind(&SimpleRemoveTransaction::readyHandler, this));
+
+ defineState(SELECTING_FWD_SERVER_ST, "SELECTING_FWD_SERVER_ST",
+ std::bind(&SimpleRemoveTransaction::selectingFwdServerHandler,
+ this));
+
+ defineState(SELECTING_REV_SERVER_ST, "SELECTING_REV_SERVER_ST",
+ std::bind(&SimpleRemoveTransaction::selectingRevServerHandler,
+ this));
+
+ defineState(REMOVING_FWD_RRS_ST, "REMOVING_FWD_RRS_ST",
+ std::bind(&SimpleRemoveTransaction::removingFwdRRsHandler,
+ this));
+
+ defineState(REMOVING_REV_PTRS_ST, "REMOVING_REV_PTRS_ST",
+ std::bind(&SimpleRemoveTransaction::removingRevPtrsHandler,
+ this));
+
+ defineState(PROCESS_TRANS_OK_ST, "PROCESS_TRANS_OK_ST",
+ std::bind(&SimpleRemoveTransaction::processRemoveOkHandler,
+ this));
+
+ defineState(PROCESS_TRANS_FAILED_ST, "PROCESS_TRANS_FAILED_ST",
+ std::bind(&SimpleRemoveTransaction::processRemoveFailedHandler,
+ this));
+}
+
+void
+SimpleRemoveTransaction::verifyStates() {
+ // Call superclass implementation first to verify its states. These are
+ // states common to all transactions, and they must be defined.
+ // READY_ST
+ // SELECTING_FWD_SERVER_ST
+ // SELECTING_REV_SERVER_ST
+ // PROCESS_TRANS_OK_ST
+ // PROCESS_TRANS_FAILED_ST
+ NameChangeTransaction::verifyStates();
+
+ // Verify SimpleRemoveTransaction states by attempting to fetch them.
+ getStateInternal(REMOVING_FWD_RRS_ST);
+ getStateInternal(REMOVING_REV_PTRS_ST);
+}
+
+void
+SimpleRemoveTransaction::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(SimpleRemoveTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+}
+
+void
+SimpleRemoveTransaction::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(SimpleRemoveTransactionError,
+ "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_RRS_ST, SERVER_SELECTED_EVT);
+ }
+ else {
+ // Server list is exhausted, so fail the transaction.
+ transition(PROCESS_TRANS_FAILED_ST, NO_MORE_SERVERS_EVT);
+ }
+}
+
+void
+SimpleRemoveTransaction::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(d2_to_dns_logger,
+ DHCP_DDNS_FORWARD_REMOVE_RRS_BUILD_FAILURE)
+ .arg(getRequestId())
+ .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("Forward RR Remove");
+ break;
+
+ case IO_COMPLETED_EVT: {
+ switch (getDnsUpdateStatus()) {
+ case DNSClient::SUCCESS: {
+ // We successfully received a response packet from the server.
+ // The RCODE will be based on a value-dependent RRset search,
+ // see RFC 2136 section 3.2.3/3.2.4.
+ const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode();
+ if ((rcode == dns::Rcode::NOERROR()) ||
+ (rcode == dns::Rcode::NXRRSET())) {
+ // We were able to remove them or they were not there (
+ // Rcode of NXRRSET means there are no matching RRsets).
+ // In either case, we consider it success and 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 {
+ // 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(d2_to_dns_logger, DHCP_DDNS_FORWARD_REMOVE_RRS_REJECTED)
+ .arg(getRequestId())
+ .arg(getCurrentServer()->toText())
+ .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(d2_to_dns_logger, DHCP_DDNS_FORWARD_REMOVE_RRS_IO_ERROR)
+ .arg(getRequestId())
+ .arg(getNcr()->getFqdn())
+ .arg(getCurrentServer()->toText());
+
+ 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(d2_to_dns_logger, DHCP_DDNS_FORWARD_REMOVE_RRS_RESP_CORRUPT)
+ .arg(getRequestId())
+ .arg(getCurrentServer()->toText())
+ .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(d2_to_dns_logger,
+ DHCP_DDNS_FORWARD_REMOVE_RRS_BAD_DNSCLIENT_STATUS)
+ .arg(getRequestId())
+ .arg(getDnsUpdateStatus())
+ .arg(getNcr()->getFqdn())
+ .arg(getCurrentServer()->toText());
+
+ 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(SimpleRemoveTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+}
+
+
+void
+SimpleRemoveTransaction::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(SimpleRemoveTransactionError,
+ "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
+SimpleRemoveTransaction::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(d2_to_dns_logger, DHCP_DDNS_REVERSE_REMOVE_BUILD_FAILURE)
+ .arg(getRequestId())
+ .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("Reverse Remove");
+ break;
+
+ case IO_COMPLETED_EVT: {
+ switch (getDnsUpdateStatus()) {
+ case DNSClient::SUCCESS: {
+ // We successfully received a response packet from the server.
+ // The RCODE will be based on a value-dependent RRset search,
+ // see RFC 2136 section 3.2.3/3.2.4.
+ const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode();
+ if ((rcode == dns::Rcode::NOERROR()) ||
+ (rcode == dns::Rcode::NXRRSET())) {
+ // We were able to remove the reverse mapping or they were
+ // not there (Rcode of NXRRSET means there are no matching
+ // RRsets). In either case, mark it as done.
+ 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(d2_to_dns_logger, DHCP_DDNS_REVERSE_REMOVE_REJECTED)
+ .arg(getRequestId())
+ .arg(getCurrentServer()->toText())
+ .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(d2_to_dns_logger, DHCP_DDNS_REVERSE_REMOVE_IO_ERROR)
+ .arg(getRequestId())
+ .arg(getNcr()->getFqdn())
+ .arg(getCurrentServer()->toText());
+
+ // 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(d2_to_dns_logger, DHCP_DDNS_REVERSE_REMOVE_RESP_CORRUPT)
+ .arg(getRequestId())
+ .arg(getCurrentServer()->toText())
+ .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(d2_to_dns_logger,
+ DHCP_DDNS_REVERSE_REMOVE_BAD_DNSCLIENT_STATUS)
+ .arg(getRequestId())
+ .arg(getDnsUpdateStatus())
+ .arg(getNcr()->getFqdn())
+ .arg(getCurrentServer()->toText());
+
+ 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(SimpleRemoveTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+}
+
+
+void
+SimpleRemoveTransaction::processRemoveOkHandler() {
+ switch(getNextEvent()) {
+ case UPDATE_OK_EVT:
+ LOG_INFO(d2_to_dns_logger, DHCP_DDNS_REMOVE_SUCCEEDED)
+ .arg(getRequestId())
+ .arg(getNcr()->toText());
+ setNcrStatus(dhcp_ddns::ST_COMPLETED);
+ endModel();
+ break;
+ default:
+ // Event is invalid.
+ isc_throw(SimpleRemoveTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+}
+
+void
+SimpleRemoveTransaction::processRemoveFailedHandler() {
+ switch(getNextEvent()) {
+ case UPDATE_FAILED_EVT:
+ case NO_MORE_SERVERS_EVT:
+ case SERVER_IO_ERROR_EVT:
+ setNcrStatus(dhcp_ddns::ST_FAILED);
+ LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_REMOVE_FAILED)
+ .arg(getRequestId())
+ .arg(transactionOutcomeString());
+ endModel();
+ break;
+ default:
+ // Event is invalid.
+ isc_throw(SimpleRemoveTransactionError,
+ "Wrong event for context: " << getContextStr());
+ }
+}
+
+void
+SimpleRemoveTransaction::buildRemoveFwdRRsRequest() {
+ // Construct an empty request.
+ D2UpdateMessagePtr request = prepNewRequest(getForwardDomain());
+
+ // There are no pre-requisites.
+
+ // Build the Update Section
+ // Construct dns::Name from NCR fqdn.
+ dns::Name fqdn(dns::Name(getNcr()->getFqdn()));
+
+ // Build the Update Section.
+
+ // Create the FQDN/IP 'delete' RR and add it 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 DHCID 'delete' RR and add it to the update section.
+ update.reset(new dns::RRset(fqdn, dns::RRClass::ANY(),
+ dns::RRType::DHCID(), dns::RRTTL(0)));
+ request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
+
+ // Set the transaction's update request to the new request.
+ setDnsUpdateRequest(request);
+}
+
+void
+SimpleRemoveTransaction::buildRemoveRevPtrsRequest() {
+ // Construct an empty request.
+ D2UpdateMessagePtr request = prepNewRequest(getReverseDomain());
+
+ // Create the reverse IP address "FQDN".
+ std::string rev_addr = D2CfgMgr::reverseIpAddress(getNcr()->getIpAddress());
+ dns::Name rev_ip(rev_addr);
+
+ // There are no pre-requisites.
+
+ // Build the Update section.
+
+ // Create the FQDN/IP PTR 'delete' RR for this IP and add it to
+ // the 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);
+
+ // Set the transaction's update request to the new request.
+ setDnsUpdateRequest(request);
+}
+
+} // namespace isc::d2
+} // namespace isc
--- /dev/null
+// Copyright (C) 2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef SIMPLE_REMOVE_H
+#define SIMPLE_REMOVE_H
+
+/// @file nc_remove.h This file defines the class SimpleRemoveTransaction.
+
+#include <d2/nc_trans.h>
+
+namespace isc {
+namespace d2 {
+
+/// @brief Thrown if the SimpleRemoveTransaction encounters a general error.
+class SimpleRemoveTransactionError : public isc::Exception {
+public:
+ SimpleRemoveTransactionError(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.
+///
+/// SimpleRemoveTransaction implements a state machine for removing a forward
+/// and/or reverse DNS mappings. This state machine follows a basic
+/// removal scheme, that does not attempt to avoid conflicts between updating
+/// clients. The 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 and DHCID RR.
+/// If it does not succeed
+/// abandon the update
+///
+/// If the request includes a reverse change:
+/// Select a reverse server
+/// Send a server a request to delete the matching PTR and DHCID RRs.
+///
+/// @endcode
+///
+/// This class derives from NameChangeTransaction from which it inherits
+/// states, events, and methods common to NameChangeRequest processing.
+class SimpleRemoveTransaction : public NameChangeTransaction {
+public:
+
+ //@{ Additional states needed for SimpleRemove state model.
+ /// @brief State that attempts to remove FQDN/IP and DHCID RRs for an FQDN
+ 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 SimpleRemove 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
+ /// @param cfg_mgr pointer to the configuration manager
+ ///
+ /// @throw SimpleRemoveTransaction error if given request is not a CHG_REMOVE,
+ /// NameChangeTransaction error for base class construction errors.
+ SimpleRemoveTransaction(asiolink::IOServicePtr& io_service,
+ dhcp_ddns::NameChangeRequestPtr& ncr,
+ DdnsDomainPtr& forward_domain,
+ DdnsDomainPtr& reverse_domain,
+ D2CfgMgrPtr& cfg_mgr);
+
+ /// @brief Destructor
+ virtual ~SimpleRemoveTransaction();
+
+protected:
+ /// @brief Adds events defined by SimpleRemoveTransaction 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. This tests that the needed events are in
+ /// the event dictionary.
+ ///
+ /// @throw StateModelError if an event value is undefined.
+ virtual void verifyEvents();
+
+ /// @brief Adds states defined by SimpleRemoveTransaction 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. This tests that the needed states are in
+ /// the state dictionary.
+ ///
+ /// @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. 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 SimpleRemoveTransactionError 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_RRS_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_RRS_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 SimpleRemoveTransactionError 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 SimpleRemoveTransactionError if upon entry next event is not
+ /// SELECT_SERVER_EVT or SERVER_IO_ERROR_EVT.
+ void selectingRevServerHandler();
+
+ /// @brief State handler for REMOVING_FWD_RRS_ST.
+ ///
+ /// Entered from:
+ /// - SELECTING_FWD_SERVER_ST with next event SEVER_SELECTED_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_COMPLETED_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.
+ ///
+ /// @throw SimpleRemoveTransactionError 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_COMPLETED_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 SimpleRemoveTransactionError 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 SimpleRemoveTransactionError 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_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 SimpleRemoveTransactionError if upon entry next event is not:
+ /// UPDATE_FAILED_EVT
+ void processRemoveFailedHandler();
+
+ /// @brief Builds a DNS request to remove all forward DNS RRs for a FQDN.
+ ///
+ /// Constructs a DNS update request, based upon the NCR, to remove the
+ /// forward DNS (A or AAAA) RR and DHCID RR. Once constructed, the request
+ /// is stored as the transaction's DNS update request.
+ ///
+ /// Prerequisite RRsets:
+ /// - None
+ ///
+ /// Updates RRsets:
+ /// -# A delete of FQDN/IP RR for the FQDN
+ /// -# A delete of DHCID RR for the FQDN
+ ///
+ /// @throw This method does not throw but underlying methods may.
+ void buildRemoveFwdRRsRequest();
+
+ /// @brief Builds a DNS request to remove a reverse DNS entry for a FQDN
+ ///
+ /// Constructs a DNS update request, based upon the NCR, for removing a
+ /// reverse DNS mapping. Once constructed, the request is stored as
+ /// the transaction's DNS update request.
+ ///
+ /// Prerequisite RRsets:
+ /// - None
+ ///
+ /// Updates RRsets:
+ /// -# A delete of PTR RR for the IP
+ /// -# A delete of DHCID RR for the IP
+ ///
+ /// @throw This method does not throw but underlying methods may.
+ void buildRemoveRevPtrsRequest();
+};
+
+/// @brief Defines a pointer to a SimpleRemoveTransaction.
+typedef boost::shared_ptr<SimpleRemoveTransaction> SimpleRemoveTransactionPtr;
+
+
+} // namespace isc::d2
+} // namespace isc
+#endif
d2_unittests_SOURCES += parser_unittest.cc parser_unittest.h
d2_unittests_SOURCES += get_config_unittest.cc
d2_unittests_SOURCES += d2_command_unittest.cc
+d2_unittests_SOURCES += simple_add_unittests.cc
+d2_unittests_SOURCES += simple_remove_unittests.cc
d2_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
d2_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS)
" \"ip-address\" : \"192.168.2.1\" , "
" \"dhcid\" : \"010203040A7F8E3D\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\" : true "
"}";
// Start queue manager with known good config.
" \"ip-address\" : \"192.168.2.1\" , "
" \"dhcid\" : \"010203040A7F8E3D\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\" : true "
"}";
// Manually enqueue a request. This lets us test logic with queue
" \"ip-address\" : \"192.168.2.1\" , "
" \"dhcid\" : \"010203040A7F8E3D\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\" : true "
"}",
// Valid Remove.
"{"
" \"ip-address\" : \"192.168.2.1\" , "
" \"dhcid\" : \"010203040A7F8E3D\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\" : true "
"}",
// Valid Add with IPv6 address
"{"
" \"ip-address\" : \"fe80::2acf:e9ff:fe12:e56f\" , "
" \"dhcid\" : \"010203040A7F8E3D\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\" : true "
"}"
};
#include <asiolink/io_service.h>
#include <d2/d2_update_mgr.h>
#include <nc_test_utils.h>
+#include <d2/nc_add.h>
+#include <d2/nc_remove.h>
+#include <d2/nc_remove.h>
+#include <d2/simple_add.h>
+#include <d2/simple_remove.h>
#include <process/testutils/d_test_stubs.h>
#include <util/time_utilities.h>
" \"ip-address\" : \"192.168.1.2\" , "
" \"dhcid\" : \"0102030405060708\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\" : true "
"}";
const char* dhcids[] = { "111111", "222222", "333333", "444444"};
ASSERT_TRUE (pos != update_mgr_->transactionListEnd());
NameChangeTransactionPtr trans = (*pos).second;
ASSERT_TRUE(trans);
+
+ // Verify the correct type of transaction was created.
+ NameAddTransaction* t = dynamic_cast<NameAddTransaction*>(trans.get());
+ ASSERT_TRUE(t);
// At this point the transaction should have constructed
// and sent the DNS request.
NameChangeTransactionPtr trans = (*pos).second;
ASSERT_TRUE(trans);
+ // Verify the correct type of transaction was created.
+ NameRemoveTransaction* t = dynamic_cast<NameRemoveTransaction*>(trans.get());
+ ASSERT_TRUE(t);
+
// At this point the transaction should have constructed
// and sent the DNS request.
ASSERT_TRUE(trans->getCurrentServer());
}
}
+/// @brief Tests integration of SimpleAddTransaction
+/// This test verifies that update manager can create and manage a
+/// SimpleAddTransaction from start to finish. It utilizes a fake server
+/// which responds to all requests sent with NOERROR, simulating a
+/// successful addition. The transaction processes both forward and
+/// reverse changes.
+TEST_F(D2UpdateMgrTest, simpleAddTransaction) {
+ // Put each transaction on the queue.
+ canned_ncrs_[0]->setChangeType(dhcp_ddns::CHG_ADD);
+ canned_ncrs_[0]->setReverseChange(true);
+ canned_ncrs_[0]->setConflictResolution(false);
+ ASSERT_NO_THROW(queue_mgr_->enqueue(canned_ncrs_[0]));
+
+ // Call sweep once, this should:
+ // 1. Dequeue the request
+ // 2. Create the transaction
+ // 3. Start the transaction
+ ASSERT_NO_THROW(update_mgr_->sweep());
+
+ // Get a copy of the transaction.
+ TransactionList::iterator pos = update_mgr_->transactionListBegin();
+ ASSERT_TRUE (pos != update_mgr_->transactionListEnd());
+ NameChangeTransactionPtr trans = (*pos).second;
+ ASSERT_TRUE(trans);
+
+ // Verify the correct type of transaction was created.
+ SimpleAddTransaction* t = dynamic_cast<SimpleAddTransaction*>(trans.get());
+ ASSERT_TRUE(t);
+
+ // At this point the transaction should have constructed
+ // and sent the DNS request.
+ ASSERT_TRUE(trans->getCurrentServer());
+ ASSERT_TRUE(trans->isModelRunning());
+ ASSERT_EQ(1, trans->getUpdateAttempts());
+ ASSERT_EQ(StateModel::NOP_EVT, trans->getNextEvent());
+
+ // Create a server based on the transaction's current server, and
+ // start it listening.
+ FauxServer server(*io_service_, *(trans->getCurrentServer()));
+ server.receive(FauxServer::USE_RCODE, dns::Rcode::NOERROR());
+
+ // Run sweep and IO until everything is done.
+ processAll();
+
+ // Verify that model succeeded.
+ EXPECT_FALSE(trans->didModelFail());
+
+ // Both completion flags should be true.
+ EXPECT_TRUE(trans->getForwardChangeCompleted());
+ EXPECT_TRUE(trans->getReverseChangeCompleted());
+
+ // Verify that we went through success state.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+ trans->getPrevState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
+ trans->getLastEvent());
+}
+
+/// @brief Tests integration of SimpleRemoveTransaction
+/// This test verifies that update manager can create and manage a
+/// SimpleRemoveTransaction from start to finish. It utilizes a fake server
+/// which responds to all requests sent with NOERROR, simulating a
+/// successful addition. The transaction processes both forward and
+/// reverse changes.
+TEST_F(D2UpdateMgrTest, simpleRemoveTransaction) {
+ // Put each transaction on the queue.
+ canned_ncrs_[0]->setChangeType(dhcp_ddns::CHG_REMOVE);
+ canned_ncrs_[0]->setReverseChange(true);
+ canned_ncrs_[0]->setConflictResolution(false);
+ ASSERT_NO_THROW(queue_mgr_->enqueue(canned_ncrs_[0]));
+
+ // Call sweep once, this should:
+ // 1. Dequeue the request
+ // 2. Create the transaction
+ // 3. Start the transaction
+ ASSERT_NO_THROW(update_mgr_->sweep());
+
+ // Get a copy of the transaction.
+ TransactionList::iterator pos = update_mgr_->transactionListBegin();
+ ASSERT_TRUE (pos != update_mgr_->transactionListEnd());
+ NameChangeTransactionPtr trans = (*pos).second;
+ ASSERT_TRUE(trans);
+
+ // Verify the correct type of transaction was created.
+ SimpleRemoveTransaction* t = dynamic_cast<SimpleRemoveTransaction*>(trans.get());
+ ASSERT_TRUE(t);
+
+ // At this point the transaction should have constructed
+ // and sent the DNS request.
+ ASSERT_TRUE(trans->getCurrentServer());
+ ASSERT_TRUE(trans->isModelRunning());
+ ASSERT_EQ(1, trans->getUpdateAttempts());
+ ASSERT_EQ(StateModel::NOP_EVT, trans->getNextEvent());
+
+ // Create a server based on the transaction's current server,
+ // and start it listening.
+ FauxServer server(*io_service_, *(trans->getCurrentServer()));
+ server.receive(FauxServer::USE_RCODE, dns::Rcode::NOERROR());
+
+ // Run sweep and IO until everything is done.
+ processAll();
+
+ // Verify that model succeeded.
+ EXPECT_FALSE(trans->didModelFail());
+
+ // Both completion flags should be true.
+ EXPECT_TRUE(trans->getForwardChangeCompleted());
+ EXPECT_TRUE(trans->getReverseChangeCompleted());
+
+ // Verify that we went through success state.
+ EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+ trans->getPrevState());
+ EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT,
+ trans->getLastEvent());
+}
+
}
" \"ip-address\" : \"192.168.2.1\" , "
" \"dhcid\" : \"0102030405060708\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\" : true "
"}";
dhcp_ddns::NameChangeRequestPtr ncr;
" \"ip-address\" : \"192.168.2.1\" , "
" \"dhcid\" : \"0102030405060708\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\" : true "
"}";
dhcp_ddns::NameChangeRequestPtr ncr;
name_remove->getNextEvent());
}
-// Tests addingFwdAddrsHandler with the following scenario:
+// Tests removingFwdAddrsHandler with the following scenario:
//
// The request includes only a forward change.
// Initial posted event is SERVER_SELECTED_EVT.
" \"ip-address\" : \"192.168.2.1\" , "
" \"dhcid\" : \"0102030405060708\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\" : true "
"}";
// Create NameChangeRequest from JSON string.
" \"ip-address\" : \"2001:1::100\" , "
" \"dhcid\" : \"0102030405060708\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\" : true "
"}";
// Create NameChangeRequest from JSON string.
return (stream.str());
}
-}; // namespace isc::d2
-}; // namespace isc
+// Verifies that the contents of the given transaction's DNS update request
+// is correct for replacing a forward DNS entry when not using conflict
+// resolution.
+void checkSimpleReplaceFwdAddressRequest(NameChangeTransaction& tran) {
+ const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest();
+ ASSERT_TRUE(request);
+
+ // Safety check.
+ dhcp_ddns::NameChangeRequestPtr ncr = tran.getNcr();
+ ASSERT_TRUE(ncr);
+
+ std::string exp_zone_name = tran.getForwardDomain()->getName();
+ std::string exp_fqdn = ncr->getFqdn();
+ const dns::RRType& exp_ip_rr_type = tran.getAddressRRType();
+
+ // Verify the zone section.
+ checkZone(request, exp_zone_name);
+
+ // Verify the PREREQUISITE SECTION
+ // There should be no prerequisites.
+ dns::RRsetPtr rrset;
+ checkRRCount(request, D2UpdateMessage::SECTION_PREREQUISITE, 0);
+
+ // Verify the UPDATE SECTION
+ // Should be 4
+ // 1. delete of the FQDN/IP RR
+ // 2. delete of the DHCID RR
+ // 3. add of the FQDN/IP RR
+ // 4. add of the DHCID RR
+ checkRRCount(request, D2UpdateMessage::SECTION_UPDATE, 4);
+
+ // Fetch ttl.
+ uint32_t ttl = ncr->getLeaseLength();
+
+ // Verify the FQDN delete RR.
+ ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+ SECTION_UPDATE, 0));
+ checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), exp_ip_rr_type, 0, ncr);
+
+ // Verify the DHCID delete RR.
+ ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+ SECTION_UPDATE, 1));
+ checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), dns::RRType::DHCID(),
+ 0, ncr);
+
+ // Verify the FQDN/IP add RR.
+ ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+ SECTION_UPDATE, 2));
+ checkRR(rrset, exp_fqdn, dns::RRClass::IN(), exp_ip_rr_type, ttl, ncr);
+
+ // Now, verify the DHCID add RR.
+ ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+ SECTION_UPDATE, 3));
+ checkRR(rrset, exp_fqdn, dns::RRClass::IN(), dns::RRType::DHCID(),
+ ttl, ncr);
+
+ // Verify there are no RRs in the ADDITIONAL Section.
+ checkRRCount(request, D2UpdateMessage::SECTION_ADDITIONAL, 0);
+
+ // Verify that it will render toWire without throwing.
+ dns::MessageRenderer renderer;
+ ASSERT_NO_THROW(request->toWire(renderer));
+}
+
+void checkSimpleRemoveFwdRRsRequest(NameChangeTransaction& tran) {
+ const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest();
+ ASSERT_TRUE(request);
+
+ // Safety check.
+ dhcp_ddns::NameChangeRequestPtr ncr = tran.getNcr();
+ ASSERT_TRUE(ncr);
+
+ std::string exp_zone_name = tran.getForwardDomain()->getName();
+ std::string exp_fqdn = ncr->getFqdn();
+ const dns::RRType& exp_ip_rr_type = tran.getAddressRRType();
+
+ // Verify the zone section.
+ checkZone(request, exp_zone_name);
+
+ // Verify there no prerequisites.
+ checkRRCount(request, D2UpdateMessage::SECTION_PREREQUISITE, 0);
+
+ // Verify there are 2 RRs in the UPDATE Section.
+ checkRRCount(request, D2UpdateMessage::SECTION_UPDATE, 2);
+
+ // Verify the FQDN delete RR.
+ dns::RRsetPtr rrset;
+ ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+ SECTION_UPDATE, 0));
+ checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), exp_ip_rr_type, 0, ncr);
+
+ // Verify the DHCID delete RR.
+ ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+ SECTION_UPDATE, 1));
+ checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), dns::RRType::DHCID(),
+ 0, ncr);
+
+ // Verify that it will render toWire without throwing.
+ dns::MessageRenderer renderer;
+ ASSERT_NO_THROW(request->toWire(renderer));
+}
+
+// Verifies that the contents of the given transaction's DNS update request
+// is correct for removing a reverse DNS entry when not using conflict
+// resolution.
+void checkSimpleRemoveRevPtrsRequest(NameChangeTransaction& tran) {
+ const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest();
+ ASSERT_TRUE(request);
+
+ // Safety check.
+ dhcp_ddns::NameChangeRequestPtr ncr = tran.getNcr();
+ ASSERT_TRUE(ncr);
+
+ std::string exp_zone_name = tran.getReverseDomain()->getName();
+ std::string exp_rev_addr = D2CfgMgr::reverseIpAddress(ncr->getIpAddress());
+
+ // Verify the zone section.
+ checkZone(request, exp_zone_name);
+
+ // Verify there are no RRs in the PREREQUISITE Section.
+ checkRRCount(request, D2UpdateMessage::SECTION_PREREQUISITE, 0);
+
+ // Verify there is 2 RRs in the UPDATE Section.
+ checkRRCount(request, D2UpdateMessage::SECTION_UPDATE, 2);
+
+ // Verify the FQDN delete RR.
+ dns::RRsetPtr rrset;
+ ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+ SECTION_UPDATE, 0));
+ checkRR(rrset, exp_rev_addr, dns::RRClass::ANY(), dns::RRType::PTR(), 0, ncr);
+
+ // Verify the DHCID delete RR.
+ ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
+ SECTION_UPDATE, 1));
+ checkRR(rrset, exp_rev_addr, dns::RRClass::ANY(), dns::RRType::DHCID(), 0, ncr);
+
+ // Verify that it will render toWire without throwing.
+ dns::MessageRenderer renderer;
+ ASSERT_NO_THROW(request->toWire(renderer));
+}
+
+// Verifies the current state and next event in a transaction
+void checkContext(NameChangeTransactionPtr trans, const int exp_state,
+ const int exp_evt, const std::string& file, int line) {
+ ASSERT_TRUE(trans);
+ ASSERT_TRUE(exp_state == trans->getCurrState() && exp_evt == trans->getNextEvent())
+ << "expected state: " << trans->getStateLabel(exp_state)
+ << " event: " << trans->getEventLabel(exp_evt)
+ << ", actual context: " << trans->getContextStr()
+ << " at " << file << ":" << line;
+}
+
+} // namespace isc::d2
+} // namespace isc
-// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
/// @param tran Transaction containing the request to be verified.
extern void checkRemoveRevPtrsRequest(NameChangeTransaction& tran);
+/// @brief Verifies a simple forward mapping replacement DNS update request
+///
+/// Tests that the DNS Update request for a given transaction, is correct for
+/// replacing a forward DNS mapping when not using conflict resolution.
+///
+/// @param tran Transaction containing the request to be verified.
+extern void checkSimpleReplaceFwdAddressRequest(NameChangeTransaction& tran);
+
+/// @brief Verifies a simple forward RR removal DNS update request
+///
+/// Tests that the DNS Update request for a given transaction, is correct for
+/// removing forward RR DNS entries when not using conflict resolution.
+///
+/// @param tran Transaction containing the request to be verified.
+extern void checkSimpleRemoveFwdRRsRequest(NameChangeTransaction& tran);
+
+/// @brief Verifies a simple reverse RR removal DNS update request
+///
+/// Tests that the DNS Update request for a given transaction, is correct for
+/// removing reverse RR DNS entries when not using conflict resolution.
+///
+/// @param tran Transaction containing the request to be verified.
+extern void checkSimpleRemoveRevPtrsRequest(NameChangeTransaction& tran);
/// @brief Creates a NameChangeRequest from JSON string.
///
/// @param len size (in bytes) of data
extern std::string toHexText(const uint8_t* data, size_t len);
-}; // namespace isc::d2
-}; // namespace isc
+/// @brief Verifies the current state and next event in a transaction
+/// @param trans NameChangeTransaction to check
+/// @param exp_state expected current state of the transaction
+/// @param exp_event expected next event of the transaction
+/// @param file source file name
+/// @param line source line number
+extern void checkContext(NameChangeTransactionPtr trans, const int exp_state,
+ const int exp_evt, const std::string& file, int line);
+
+/// @brief Macro for calling checkContext() that supplies invocation location
+#define CHECK_CONTEXT(a,b,c) checkContext(a,b,c,__FILE__,__LINE__)
+
+
+} // namespace isc::d2
+} // namespace isc
#endif
" \"ip-address\" : \"192.168.2.1\" , "
" \"dhcid\" : \"0102030405060708\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\" : true "
"}";
dhcp_ddns::NameChangeRequestPtr ncr;
--- /dev/null
+// Copyright (C) 2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <asiolink/io_service.h>
+#include <d2/d2_cfg_mgr.h>
+#include <d2/d2_cfg_mgr.h>
+#include <d2/simple_add.h>
+#include <dns/messagerenderer.h>
+#include <nc_test_utils.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::d2;
+using namespace isc::util;
+
+namespace {
+
+/// @brief Test class derived from SimpleAddTransaction to provide visibility
+// to protected methods.
+class SimpleAddStub : public SimpleAddTransaction {
+public:
+ SimpleAddStub(asiolink::IOServicePtr& io_service,
+ dhcp_ddns::NameChangeRequestPtr& ncr,
+ DdnsDomainPtr& forward_domain,
+ DdnsDomainPtr& reverse_domain,
+ D2CfgMgrPtr& cfg_mgr)
+ : SimpleAddTransaction(io_service, ncr, forward_domain, reverse_domain,
+ cfg_mgr),
+ simulate_send_exception_(false),
+ simulate_build_request_exception_(false) {
+ }
+
+ virtual ~SimpleAddStub() {
+ }
+
+ /// @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 comment Parameter is unused, but present in base class method.
+ ///
+ virtual void sendUpdate(const std::string& /*comment*/) {
+ 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 NameChangeTransaction 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 (SimpleAddTransactionError,
+ "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 exercised 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().get() != 0);
+ }
+
+ 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().get() != 0);
+ }
+
+ 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 SimpleAddTransaction::defineEvents;
+ using SimpleAddTransaction::verifyEvents;
+ using SimpleAddTransaction::defineStates;
+ using SimpleAddTransaction::verifyStates;
+ using SimpleAddTransaction::readyHandler;
+ using SimpleAddTransaction::selectingFwdServerHandler;
+ using SimpleAddTransaction::getCurrentServer;
+ using SimpleAddTransaction::setDnsUpdateStatus;
+ using SimpleAddTransaction::replacingFwdAddrsHandler;
+ using SimpleAddTransaction::selectingRevServerHandler;
+ using SimpleAddTransaction::replacingRevPtrsHandler;
+ using SimpleAddTransaction::processAddOkHandler;
+ using SimpleAddTransaction::processAddFailedHandler;
+ using SimpleAddTransaction::buildReplaceFwdAddressRequest;
+ using SimpleAddTransaction::buildReplaceRevPtrsRequest;
+};
+
+typedef boost::shared_ptr<SimpleAddStub> SimpleAddStubPtr;
+
+/// @brief Test fixture for testing SimpleAddTransaction
+///
+/// Note this class uses SimpleAddStub class to exercise non-public
+/// aspects of SimpleAddTransaction.
+class SimpleAddTransactionTest : public TransactionTest {
+public:
+
+ SimpleAddTransactionTest() {
+ }
+
+ virtual ~SimpleAddTransactionTest() {
+ }
+
+ /// @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
+ SimpleAddStubPtr makeTransaction4(int change_mask = FWD_AND_REV_CHG) {
+ // Creates IPv4 remove request, forward, and reverse domains.
+ setupForIPv4Transaction(dhcp_ddns::CHG_ADD, change_mask);
+
+ // Now create the test transaction as would occur in update manager.
+ return (SimpleAddStubPtr(new SimpleAddStub(io_service_, ncr_,
+ forward_domain_,
+ reverse_domain_, cfg_mgr_)));
+ }
+
+ /// @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
+ SimpleAddStubPtr makeTransaction6(int change_mask = FWD_AND_REV_CHG) {
+ // Creates IPv6 remove request, forward, and reverse domains.
+ setupForIPv6Transaction(dhcp_ddns::CHG_ADD, change_mask);
+
+ // Now create the test transaction as would occur in update manager.
+ return (SimpleAddStubPtr(new SimpleAddStub(io_service_, ncr_,
+ forward_domain_,
+ reverse_domain_,
+ cfg_mgr_)));
+ }
+
+ /// @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.
+ SimpleAddStubPtr prepHandlerTest(unsigned int state, unsigned int event,
+ unsigned int change_mask = FWD_AND_REV_CHG,
+ short family = AF_INET) {
+ SimpleAddStubPtr name_add = (family == AF_INET ?
+ makeTransaction4(change_mask) :
+ makeTransaction6(change_mask));
+ name_add->initDictionaries();
+ name_add->postNextEvent(event);
+ name_add->setState(state);
+ return (name_add);
+ }
+};
+
+/// @brief Tests SimpleAddTransaction construction.
+/// This test verifies that:
+/// 1. Construction with invalid type of request
+/// 2. Valid construction functions properly
+TEST(SimpleAddTransaction, construction) {
+ asiolink::IOServicePtr io_service(new isc::asiolink::IOService());
+ D2CfgMgrPtr cfg_mgr(new D2CfgMgr());
+
+ const char* msg_str =
+ "{"
+ " \"change-type\" : 1 , "
+ " \"forward-change\" : true , "
+ " \"reverse-change\" : true , "
+ " \"fqdn\" : \"example.com.\" , "
+ " \"ip-address\" : \"192.168.2.1\" , "
+ " \"dhcid\" : \"0102030405060708\" , "
+ " \"lease-expires-on\" : \"20130121132405\" , "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\" : true "
+ "}";
+
+ 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(SimpleAddTransaction(io_service, ncr,
+ forward_domain, reverse_domain, cfg_mgr),
+ SimpleAddTransactionError);
+
+ // Verify that a valid construction attempt works.
+ ncr->setChangeType(isc::dhcp_ddns::CHG_ADD);
+ EXPECT_NO_THROW(SimpleAddTransaction(io_service, ncr,
+ forward_domain, reverse_domain,
+ cfg_mgr));
+}
+
+/// @brief Tests event and state dictionary construction and verification.
+TEST_F(SimpleAddTransactionTest, dictionaryCheck) {
+ SimpleAddStubPtr name_add;
+ ASSERT_NO_THROW(name_add = makeTransaction4());
+ // Verify that the event and state dictionary validation fails prior
+ // dictionary construction.
+ ASSERT_THROW(name_add->verifyEvents(), StateModelError);
+ ASSERT_THROW(name_add->verifyStates(), StateModelError);
+
+ // Construct both dictionaries.
+ ASSERT_NO_THROW(name_add->defineEvents());
+ ASSERT_NO_THROW(name_add->defineStates());
+
+ // Verify both event and state dictionaries now pass validation.
+ ASSERT_NO_THROW(name_add->verifyEvents());
+ ASSERT_NO_THROW(name_add->verifyStates());
+}
+
+/// @brief Tests construction of a DNS update request for replacing a forward
+/// dns entry.
+TEST_F(SimpleAddTransactionTest, buildReplaceFwdAddressRequest) {
+ // Create a IPv4 forward replace transaction.
+ // Verify the request builds without error.
+ // and then verify the request contents.
+ SimpleAddStubPtr name_add;
+ ASSERT_NO_THROW(name_add = makeTransaction4());
+ ASSERT_NO_THROW(name_add->buildReplaceFwdAddressRequest());
+ checkSimpleReplaceFwdAddressRequest(*name_add);
+
+ // Create a IPv6 forward replace transaction.
+ // Verify the request builds without error.
+ // and then verify the request contents.
+ ASSERT_NO_THROW(name_add = makeTransaction6());
+ ASSERT_NO_THROW(name_add->buildReplaceFwdAddressRequest());
+ checkSimpleReplaceFwdAddressRequest(*name_add);
+}
+
+/// @brief Tests the construction of a DNS update request for replacing a
+/// reverse dns entry.
+TEST_F(SimpleAddTransactionTest, buildReplaceRevPtrsRequest) {
+ // Create a IPv4 reverse replace transaction.
+ // Verify the request builds without error.
+ // and then verify the request contents.
+ SimpleAddStubPtr name_add;
+ ASSERT_NO_THROW(name_add = makeTransaction4());
+ ASSERT_NO_THROW(name_add->buildReplaceRevPtrsRequest());
+ checkReplaceRevPtrsRequest(*name_add);
+
+ // Create a IPv6 reverse replace transaction.
+ // Verify the request builds without error.
+ // and then verify the request contents.
+ ASSERT_NO_THROW(name_add = makeTransaction6());
+ ASSERT_NO_THROW(name_add->buildReplaceRevPtrsRequest());
+ checkReplaceRevPtrsRequest(*name_add);
+}
+
+// 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(SimpleAddTransactionTest, readyHandler) {
+ SimpleAddStubPtr name_add;
+
+ // Create a transaction which includes only a forward change.
+ ASSERT_NO_THROW(
+ name_add = prepHandlerTest(NameChangeTransaction::READY_ST,
+ StateModel::START_EVT, FORWARD_CHG)
+ );
+
+ // Run readyHandler.
+ EXPECT_NO_THROW(name_add->readyHandler());
+
+ // Verify that a request requiring only a forward change, transitions to
+ // selecting a forward server.
+ CHECK_CONTEXT(name_add, NameChangeTransaction::SELECTING_FWD_SERVER_ST,
+ NameChangeTransaction::SELECT_SERVER_EVT);
+
+ // Create a transaction which includes both a forward and a reverse change.
+ ASSERT_NO_THROW(
+ name_add = prepHandlerTest(NameChangeTransaction::READY_ST,
+ StateModel::START_EVT, FWD_AND_REV_CHG)
+ );
+
+ // Run readyHandler.
+ EXPECT_NO_THROW(name_add->readyHandler());
+
+ // Verify that a request requiring both forward and reverse, starts with
+ // the forward change by transitioning to selecting a forward server.
+ CHECK_CONTEXT(name_add, NameChangeTransaction::SELECTING_FWD_SERVER_ST,
+ NameChangeTransaction::SELECT_SERVER_EVT);
+
+ // Create and prep a reverse only transaction.
+ ASSERT_NO_THROW(
+ name_add = prepHandlerTest(NameChangeTransaction::READY_ST,
+ StateModel::START_EVT, REVERSE_CHG)
+ );
+
+ // Run readyHandler.
+ EXPECT_NO_THROW(name_add->readyHandler());
+
+ // Verify that a request requiring only a reverse change, transitions to
+ // selecting a reverse server.
+ CHECK_CONTEXT(name_add, NameChangeTransaction::SELECTING_REV_SERVER_ST,
+ NameChangeTransaction::SELECT_SERVER_EVT);
+
+ // Create and prep transaction, poised to run the handler but with an
+ // invalid event.
+ ASSERT_NO_THROW(
+ name_add = prepHandlerTest(NameChangeTransaction::READY_ST, StateModel::NOP_EVT)
+ );
+
+ // Running the readyHandler should throw.
+ EXPECT_THROW(name_add->readyHandler(), SimpleAddTransactionError);
+}
+
+// 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(SimpleAddTransactionTest, selectingFwdServerHandler) {
+ SimpleAddStubPtr name_add;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(
+ name_add = 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_add->getForwardDomain()->getServers()->size();
+ for (int i = 0; i < num_servers; ++i) {
+ SCOPED_TRACE(testing::Message() << " num_servers: " << num_servers
+ << " selections: " << i);
+ // Run selectingFwdServerHandler.
+ ASSERT_NO_THROW(name_add->selectingFwdServerHandler());
+
+ // Verify that a server was selected.
+ ASSERT_TRUE(name_add->getCurrentServer());
+
+ // Verify that we transitioned correctly.
+ CHECK_CONTEXT(name_add, SimpleAddTransaction::REPLACING_FWD_ADDRS_ST,
+ NameChangeTransaction::SERVER_SELECTED_EVT);
+
+ // Post a server IO error event. This simulates an IO error occurring
+ // and a need to select the new server.
+ ASSERT_NO_THROW(name_add->postNextEvent(NameChangeTransaction::
+ SERVER_IO_ERROR_EVT));
+ }
+
+ // We should have exhausted the list of servers. Processing another
+ // SERVER_IO_ERROR_EVT should transition us to failure.
+ EXPECT_NO_THROW(name_add->selectingFwdServerHandler());
+ CHECK_CONTEXT(name_add, NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ NameChangeTransaction::NO_MORE_SERVERS_EVT);
+
+ // Create and prep transaction, poised to run the handler but with an
+ // invalid event.
+ ASSERT_NO_THROW(
+ name_add = prepHandlerTest(NameChangeTransaction::SELECTING_FWD_SERVER_ST,
+ StateModel::NOP_EVT)
+ );
+
+ // Running the handler should throw.
+ EXPECT_THROW(name_add->selectingFwdServerHandler(),
+ SimpleAddTransactionError);
+}
+
+// ************************ replacingFwdAddrHandler Tests *****************
+
+// Tests that replacingFwdAddrsHandler rejects invalid events.
+TEST_F(SimpleAddTransactionTest, replacingFwdAddrsHandler_InvalidEvent) {
+ SimpleAddStubPtr name_add;
+ // Create and prep a transaction, poised to run the handler but with
+ // an invalid event.
+ ASSERT_NO_THROW(
+ name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_FWD_ADDRS_ST,
+ NameChangeTransaction::StateModel::NOP_EVT)
+ );
+
+ // Running the handler should throw.
+ EXPECT_THROW(name_add->replacingFwdAddrsHandler(), SimpleAddTransactionError);
+}
+
+// Tests replacingFwdAddrsHandler 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(SimpleAddTransactionTest, replacingFwdAddrsHandler_FwdOnlyAddOK) {
+ SimpleAddStubPtr name_add;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(
+ name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_FWD_ADDRS_ST,
+ SimpleAddTransaction::SERVER_SELECTED_EVT, FORWARD_CHG)
+ );
+
+ // Should not be an update message yet.
+ D2UpdateMessagePtr update_msg = name_add->getDnsUpdateRequest();
+ ASSERT_FALSE(update_msg);
+
+ // At this point completion flags should be false.
+ EXPECT_FALSE(name_add->getForwardChangeCompleted());
+ EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+ // Run replacingFwdAddrsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+ // Verify that an update message was constructed properly.
+ checkSimpleReplaceFwdAddressRequest(*name_add);
+
+ // 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.
+ CHECK_CONTEXT(name_add, SimpleAddTransaction::REPLACING_FWD_ADDRS_ST,
+ NameChangeTransaction::NOP_EVT);
+
+ // Simulate receiving a successful update response.
+ name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+ // Run replacingFwdAddrsHandler again to process the response.
+ EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+ // Forward completion should be true, reverse should be false.
+ EXPECT_TRUE(name_add->getForwardChangeCompleted());
+ EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+ // Since it is a forward only change, we should be done.
+ // Verify that we transitioned correctly.
+ CHECK_CONTEXT(name_add, NameChangeTransaction::PROCESS_TRANS_OK_ST,
+ NameChangeTransaction::UPDATE_OK_EVT);
+}
+
+// Tests replacingFwdAddrsHandler 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(SimpleAddTransactionTest, replacingFwdAddrsHandler_OtherRcode) {
+ SimpleAddStubPtr name_add;
+ // Create the transaction.
+ ASSERT_NO_THROW(
+ name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_FWD_ADDRS_ST,
+ SimpleAddTransaction::SERVER_SELECTED_EVT, FWD_AND_REV_CHG)
+ );
+
+ // Select a server to satisfy log statements.
+ ASSERT_TRUE(name_add->selectFwdServer());
+
+ // Run replacingFwdAddrsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+ // Simulate receiving server rejection response. Per RFC, anything other
+ // than no error or FQDN in use is failure. Arbitrarily choosing refused.
+ name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED());
+
+ // Run replacingFwdAddrsHandler again to process the response.
+ EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+ // Completion flags should still be false.
+ EXPECT_FALSE(name_add->getForwardChangeCompleted());
+ EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+ // We should have failed the transaction. Verify that we transitioned
+ // correctly.
+ CHECK_CONTEXT(name_add, NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ NameChangeTransaction::UPDATE_FAILED_EVT);
+}
+
+// Tests replacingFwdAddrsHandler 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(SimpleAddTransactionTest, replacingFwdAddrsHandler_Timeout) {
+ SimpleAddStubPtr name_add;
+
+ // Create the transaction.
+ ASSERT_NO_THROW(
+ name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_FWD_ADDRS_ST,
+ SimpleAddTransaction::SERVER_SELECTED_EVT, FWD_AND_REV_CHG)
+ );
+
+ // Select a server to satisfy log statements.
+ ASSERT_TRUE(name_add->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_add->getDnsUpdateRequest();
+
+ // Run replacingFwdAddrsHandler to send the request.
+ EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+ const D2UpdateMessagePtr curr_msg = name_add->getDnsUpdateRequest();
+ if (i == 1) {
+ // First time out 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_add->setDnsUpdateStatus(DNSClient::TIMEOUT);
+ name_add->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+ // Run replacingFwdAddrsHandler again to process the response.
+ EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_add->getForwardChangeCompleted());
+ EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+ if (i < max_tries) {
+ // We should be ready to try again.
+ CHECK_CONTEXT(name_add, SimpleAddTransaction::REPLACING_FWD_ADDRS_ST,
+ NameChangeTransaction::SERVER_SELECTED_EVT);
+ } else {
+ // Server retries should be exhausted, time for a new server.
+ CHECK_CONTEXT(name_add, SimpleAddTransaction::SELECTING_FWD_SERVER_ST,
+ NameChangeTransaction::SERVER_IO_ERROR_EVT);
+ }
+ }
+}
+
+// Tests replacingFwdAddrsHandler 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(SimpleAddTransactionTest, replacingFwdAddrsHandler_CorruptResponse) {
+ SimpleAddStubPtr name_add;
+
+ // Create the transaction.
+ ASSERT_NO_THROW(
+ name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_FWD_ADDRS_ST,
+ SimpleAddTransaction::SERVER_SELECTED_EVT, FWD_AND_REV_CHG)
+ );
+
+ // Select a server to satisfy log statements.
+ ASSERT_TRUE(name_add->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_add->getDnsUpdateRequest();
+
+ // Run replacingFwdAddrsHandler to send the request.
+ EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+ const D2UpdateMessagePtr curr_msg = name_add->getDnsUpdateRequest();
+ if (i == 1) {
+ // First time out 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_add->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE);
+ name_add->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+ // Run replacingFwdAddrsHandler again to process the response.
+ EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_add->getForwardChangeCompleted());
+ EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+ if (i < max_tries) {
+ // We should be ready to try again.
+ CHECK_CONTEXT(name_add, SimpleAddTransaction::REPLACING_FWD_ADDRS_ST,
+ NameChangeTransaction::SERVER_SELECTED_EVT);
+ } else {
+ // Server retries should be exhausted, time for a new server.
+ CHECK_CONTEXT(name_add, SimpleAddTransaction::SELECTING_FWD_SERVER_ST,
+ NameChangeTransaction::SERVER_IO_ERROR_EVT);
+ }
+ }
+}
+
+// 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(SimpleAddTransactionTest, selectingRevServerHandler) {
+ SimpleAddStubPtr name_add;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(
+ name_add = 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_add->getReverseDomain()->getServers()->size();
+ for (int i = 0; i < num_servers; ++i) {
+ SCOPED_TRACE(testing::Message() << " num_servers: " << num_servers
+ << " selections: " << i);
+ // Run selectingRevServerHandler.
+ ASSERT_NO_THROW(name_add->selectingRevServerHandler());
+
+ // Verify that a server was selected.
+ ASSERT_TRUE(name_add->getCurrentServer());
+
+ // Verify that we transitioned correctly.
+ CHECK_CONTEXT(name_add, SimpleAddTransaction::REPLACING_REV_PTRS_ST,
+ NameChangeTransaction::SERVER_SELECTED_EVT);
+
+ // Post a server IO error event. This simulates an IO error occurring
+ // and a need to select the new server.
+ ASSERT_NO_THROW(name_add->postNextEvent(NameChangeTransaction::
+ SERVER_IO_ERROR_EVT));
+ }
+
+ // We should have exhausted the list of servers. Processing another
+ // SERVER_IO_ERROR_EVT should transition us to failure.
+ EXPECT_NO_THROW(name_add->selectingRevServerHandler());
+ CHECK_CONTEXT(name_add, NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ NameChangeTransaction::NO_MORE_SERVERS_EVT);
+
+ // Create and prep transaction, poised to run the handler but with an
+ // invalid event.
+ ASSERT_NO_THROW(
+ name_add = prepHandlerTest(NameChangeTransaction::SELECTING_REV_SERVER_ST,
+ StateModel::NOP_EVT)
+ );
+
+ // Running the handler should throw.
+ EXPECT_THROW(name_add->selectingRevServerHandler(),
+ SimpleAddTransactionError);
+}
+
+//************************** replacingRevPtrsHandler tests *****************
+
+// Tests that replacingRevPtrsHandler rejects invalid events.
+TEST_F(SimpleAddTransactionTest, replacingRevPtrsHandler_InvalidEvent) {
+ SimpleAddStubPtr name_add;
+ // Create and prep a transaction, poised to run the handler but with
+ // an invalid event.
+ ASSERT_NO_THROW(
+ name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_REV_PTRS_ST,
+ NameChangeTransaction::StateModel::NOP_EVT)
+ );
+
+ // Running the handler should throw.
+ EXPECT_THROW(name_add->replacingRevPtrsHandler(),
+ SimpleAddTransactionError);
+}
+
+// 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(SimpleAddTransactionTest, replacingRevPtrsHandler_FwdOnlyAddOK) {
+ SimpleAddStubPtr name_add;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(
+ name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_REV_PTRS_ST,
+ SimpleAddTransaction::SERVER_SELECTED_EVT, REVERSE_CHG)
+ );
+
+ // Should not be an update message yet.
+ D2UpdateMessagePtr update_msg = name_add->getDnsUpdateRequest();
+ ASSERT_FALSE(update_msg);
+
+ // At this point completion flags should be false.
+ EXPECT_FALSE(name_add->getForwardChangeCompleted());
+ EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+ // Run replacingRevPtrsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
+
+ // Verify that an update message was constructed properly.
+ checkReplaceRevPtrsRequest(*name_add);
+
+ // 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.
+ CHECK_CONTEXT(name_add, SimpleAddTransaction::REPLACING_REV_PTRS_ST,
+ NameChangeTransaction::NOP_EVT);
+
+ // Simulate receiving a successful update response.
+ name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR());
+
+ // Run replacingRevPtrsHandler again to process the response.
+ EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
+
+ // Forward completion should be false, reverse should be true.
+ EXPECT_FALSE(name_add->getForwardChangeCompleted());
+ EXPECT_TRUE(name_add->getReverseChangeCompleted());
+
+ // Since it is a reverse change, we should be done.
+ // Verify that we transitioned correctly.
+ CHECK_CONTEXT(name_add, NameChangeTransaction::PROCESS_TRANS_OK_ST,
+ NameChangeTransaction::UPDATE_OK_EVT);
+}
+
+// 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 the update was rejected.
+//
+TEST_F(SimpleAddTransactionTest, replacingRevPtrsHandler_OtherRcode) {
+ SimpleAddStubPtr name_add;
+ // Create the transaction.
+ ASSERT_NO_THROW(
+ name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_REV_PTRS_ST,
+ SimpleAddTransaction::SERVER_SELECTED_EVT, REVERSE_CHG)
+ );
+
+ // Select a server to satisfy log statements.
+ ASSERT_TRUE(name_add->selectRevServer());
+
+ // Run replacingRevPtrsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
+
+ // Simulate receiving server rejection response. Per RFC, anything other
+ // than no error is failure. Arbitrarily choosing refused.
+ name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED());
+
+ // Run replacingRevPtrsHandler again to process the response.
+ EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
+
+ // Completion flags should still be false.
+ EXPECT_FALSE(name_add->getForwardChangeCompleted());
+ EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+ // We should have failed the transaction. Verify that we transitioned
+ // correctly.
+ CHECK_CONTEXT(name_add, NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ NameChangeTransaction::UPDATE_FAILED_EVT);
+}
+
+// Tests replacingRevPtrsHandler 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(SimpleAddTransactionTest, replacingRevPtrsHandler_Timeout) {
+ SimpleAddStubPtr name_add;
+ // Create the transaction.
+ ASSERT_NO_THROW(
+ name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_REV_PTRS_ST,
+ SimpleAddTransaction::SERVER_SELECTED_EVT, REVERSE_CHG)
+ );
+
+ // Select a server to satisfy log statements.
+ ASSERT_TRUE(name_add->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_add->getDnsUpdateRequest();
+
+ // Run replacingRevPtrsHandler to send the request.
+ EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
+
+ const D2UpdateMessagePtr curr_msg = name_add->getDnsUpdateRequest();
+ if (i == 1) {
+ // First time out 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_add->setDnsUpdateStatus(DNSClient::TIMEOUT);
+ name_add->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+ // Run replacingRevPtrsHandler again to process the response.
+ EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_add->getForwardChangeCompleted());
+ EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+ if (i < max_tries) {
+ // We should be ready to try again.
+ CHECK_CONTEXT(name_add, SimpleAddTransaction::REPLACING_REV_PTRS_ST,
+ NameChangeTransaction::SERVER_SELECTED_EVT);
+ } else {
+ // Server retries should be exhausted, time for a new server.
+ CHECK_CONTEXT(name_add, SimpleAddTransaction::SELECTING_REV_SERVER_ST,
+ NameChangeTransaction::SERVER_IO_ERROR_EVT);
+ }
+ }
+}
+
+// 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 but a corrupt response is received, this occurs
+// MAX_UPDATE_TRIES_PER_SERVER times.
+//
+TEST_F(SimpleAddTransactionTest, replacingRevPtrsHandler_CorruptResponse) {
+ SimpleAddStubPtr name_add;
+ // Create the transaction.
+ ASSERT_NO_THROW(
+ name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_REV_PTRS_ST,
+ SimpleAddTransaction::SERVER_SELECTED_EVT, REVERSE_CHG)
+ );
+
+ // Select a server to satisfy log statements.
+ ASSERT_TRUE(name_add->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_add->getDnsUpdateRequest();
+
+ // Run replacingRevPtrsHandler to send the request.
+ EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
+
+ const D2UpdateMessagePtr curr_msg = name_add->getDnsUpdateRequest();
+ if (i == 1) {
+ // First time out 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_add->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE);
+ name_add->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT);
+
+ // Run replacingRevPtrsHandler again to process the response.
+ EXPECT_NO_THROW(name_add->replacingRevPtrsHandler());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_add->getForwardChangeCompleted());
+ EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+ if (i < max_tries) {
+ // We should be ready to try again.
+ CHECK_CONTEXT(name_add, SimpleAddTransaction::REPLACING_REV_PTRS_ST,
+ NameChangeTransaction::SERVER_SELECTED_EVT);
+ } else {
+ // Server retries should be exhausted, time for a new server.
+ CHECK_CONTEXT(name_add, SimpleAddTransaction::SELECTING_REV_SERVER_ST,
+ NameChangeTransaction::SERVER_IO_ERROR_EVT);
+ }
+ }
+}
+
+// Tests the processAddOkHandler functionality.
+// It verifies behavior for the following scenarios:
+//
+// 1. Posted event is UPDATE_OK_EVT
+// 2. Posted event is invalid
+//
+TEST_F(SimpleAddTransactionTest, processAddOkHandler) {
+ SimpleAddStubPtr name_add;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(
+ name_add = prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+ NameChangeTransaction::UPDATE_OK_EVT)
+ );
+
+ // Run processAddOkHandler.
+ EXPECT_NO_THROW(name_add->processAddOkHandler());
+
+ // Verify that a server was selected.
+ EXPECT_EQ(dhcp_ddns::ST_COMPLETED, name_add->getNcrStatus());
+
+ // Verify that the model has ended.
+ CHECK_CONTEXT(name_add, StateModel::END_ST, StateModel::END_EVT);
+
+ // Create and prep transaction, poised to run the handler but with an
+ // invalid event.
+ ASSERT_NO_THROW(
+ name_add = prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_OK_ST,
+ StateModel::NOP_EVT)
+ );
+
+ // Running the handler should throw.
+ EXPECT_THROW(name_add->processAddOkHandler(), SimpleAddTransactionError);
+}
+
+// Tests the processAddFailedHandler functionality.
+// It verifies behavior for the following scenarios:
+//
+// 1. Posted event is UPDATE_FAILED_EVT
+// 2. Posted event is invalid
+//
+TEST_F(SimpleAddTransactionTest, processAddFailedHandler) {
+ SimpleAddStubPtr name_add;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(
+ name_add = prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ NameChangeTransaction::UPDATE_FAILED_EVT)
+ );
+
+ // Run processAddFailedHandler.
+ EXPECT_NO_THROW(name_add->processAddFailedHandler());
+
+ // Verify that a server was selected.
+ EXPECT_EQ(dhcp_ddns::ST_FAILED, name_add->getNcrStatus());
+
+ // Verify that the model has ended. (Remember, the transaction failed NOT
+ // the model. The model should have ended normally.)
+ CHECK_CONTEXT(name_add, StateModel::END_ST, StateModel::END_EVT);
+
+ // Create and prep transaction, poised to run the handler but with an
+ // invalid event.
+ ASSERT_NO_THROW(
+ name_add = prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ StateModel::NOP_EVT)
+ );
+
+ // Running the handler should throw.
+ EXPECT_THROW(name_add->processAddFailedHandler(), SimpleAddTransactionError);
+}
+
+// Tests the processAddFailedHandler functionality.
+// It verifies behavior for posted event of NO_MORE_SERVERS_EVT.
+TEST_F(SimpleAddTransactionTest, processAddFailedHandler_NoMoreServers) {
+ SimpleAddStubPtr name_add;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(
+ name_add = prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ NameChangeTransaction::NO_MORE_SERVERS_EVT)
+ );
+
+ // Run processAddFailedHandler.
+ EXPECT_NO_THROW(name_add->processAddFailedHandler());
+
+ // Verify that a server was selected.
+ EXPECT_EQ(dhcp_ddns::ST_FAILED, name_add->getNcrStatus());
+
+ // Verify that the model has ended. (Remember, the transaction failed NOT
+ // the model. The model should have ended normally.)
+ CHECK_CONTEXT(name_add, StateModel::END_ST, StateModel::END_EVT);
+}
+
+// Tests replacingFwdAddrsHandler 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(SimpleAddTransactionTest, replacingFwdAddrsHandler_SendUpdateException) {
+ SimpleAddStubPtr name_add;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(
+ name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_FWD_ADDRS_ST,
+ NameChangeTransaction::SERVER_SELECTED_EVT, FORWARD_CHG)
+ );
+
+ name_add->simulate_send_exception_ = true;
+
+ // Run replacingFwdAddrsHandler to construct and send the request.
+ ASSERT_NO_THROW(name_add->replacingFwdAddrsHandler());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_add->getForwardChangeCompleted());
+ EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+ // Since IO exceptions should be gracefully handled, any that occur
+ // are unanticipated, and deemed unrecoverable, so the transaction should
+ // be transitioned to failure.
+ CHECK_CONTEXT(name_add, NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ NameChangeTransaction::UPDATE_FAILED_EVT);
+}
+
+// Tests replacingRevPtrHandler 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(SimpleAddTransactionTest, replacingRevPtrsHandler_SendUpdateException) {
+ SimpleAddStubPtr name_add;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(
+ name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_REV_PTRS_ST,
+ NameChangeTransaction::SERVER_SELECTED_EVT, REVERSE_CHG)
+ );
+
+ name_add->simulate_send_exception_ = true;
+
+ // Run replacingRevPtrsHandler to construct and send the request.
+ ASSERT_NO_THROW(name_add->replacingRevPtrsHandler());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_add->getForwardChangeCompleted());
+ EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+ // Since IO exceptions should be gracefully handled, any that occur
+ // are unanticipated, and deemed unrecoverable, so the transaction should
+ // be transitioned to failure.
+ CHECK_CONTEXT(name_add, NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ NameChangeTransaction::UPDATE_FAILED_EVT);
+}
+
+// Tests replacingFwdAddrsHandler 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(SimpleAddTransactionTest, replacingFwdAddrsHandler_BuildRequestException) {
+ SimpleAddStubPtr name_add;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(
+ name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_FWD_ADDRS_ST,
+ NameChangeTransaction::SERVER_SELECTED_EVT, FORWARD_CHG)
+ );
+
+ // Set the one-shot exception simulation flag.
+ name_add->simulate_build_request_exception_ = true;
+
+ // Run replacingFwdAddrsHandler 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_add->replacingFwdAddrsHandler());
+
+ // Verify we did not attempt to send anything.
+ EXPECT_EQ(0, name_add->getUpdateAttempts());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_add->getForwardChangeCompleted());
+ EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+ // Since IO exceptions should be gracefully handled, any that occur
+ // are unanticipated, and deemed unrecoverable, so the transaction should
+ // be transitioned to failure.
+ CHECK_CONTEXT(name_add, NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ NameChangeTransaction::UPDATE_FAILED_EVT);
+}
+
+// Tests replacingRevPtrHandler with the following scenario:
+//
+// The request includes only a reverse change.
+// Initial posted event is SERVER_SELECTED_EVT.
+// The request build fails due to an unexpected exception.
+//
+TEST_F(SimpleAddTransactionTest, replacingRevPtrsHandler_BuildRequestException) {
+ SimpleAddStubPtr name_add;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(
+ name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_REV_PTRS_ST,
+ NameChangeTransaction::SERVER_SELECTED_EVT, REVERSE_CHG)
+ );
+
+ // Set the one-shot exception simulation flag.
+ name_add->simulate_build_request_exception_ = true;
+
+ // Run replacingRevPtrsHandler 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_add->replacingRevPtrsHandler());
+
+ // Verify we did not attempt to send anything.
+ EXPECT_EQ(0, name_add->getUpdateAttempts());
+
+ // Completion flags should be false.
+ EXPECT_FALSE(name_add->getForwardChangeCompleted());
+ EXPECT_FALSE(name_add->getReverseChangeCompleted());
+
+ // Since IO exceptions should be gracefully handled, any that occur
+ // are unanticipated, and deemed unrecoverable, so the transaction should
+ // be transitioned to failure.
+ CHECK_CONTEXT(name_add, NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ NameChangeTransaction::UPDATE_FAILED_EVT);
+}
+
+}
--- /dev/null
+// Copyright (C) 2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <asiolink/io_service.h>
+#include <d2/d2_cfg_mgr.h>
+#include <d2/d2_cfg_mgr.h>
+#include <d2/simple_remove.h>
+#include <dns/messagerenderer.h>
+#include <nc_test_utils.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::d2;
+using namespace isc::util;
+
+
+namespace {
+
+/// @brief Test class derived from SimpleRemoveTransaction to provide visibility
+// to protected methods.
+class SimpleRemoveStub : public SimpleRemoveTransaction {
+public:
+ SimpleRemoveStub(asiolink::IOServicePtr& io_service,
+ dhcp_ddns::NameChangeRequestPtr& ncr,
+ DdnsDomainPtr& forward_domain,
+ DdnsDomainPtr& reverse_domain,
+ D2CfgMgrPtr& cfg_mgr)
+ : SimpleRemoveTransaction(io_service, ncr, forward_domain,
+ reverse_domain, cfg_mgr),
+ simulate_send_exception_(false),
+ simulate_build_request_exception_(false) {
+ }
+
+ virtual ~SimpleRemoveStub() {
+ }
+
+ /// @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 comment Parameter is unused, but present in base class method
+ ///
+ virtual void sendUpdate(const std::string& /* comment */) {
+ 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 NameChangeTransaction 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 (SimpleRemoveTransactionError,
+ "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 exercised 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().get() != 0);
+ }
+
+ 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().get() != 0);
+ }
+
+ 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 SimpleRemoveTransaction::defineEvents;
+ using SimpleRemoveTransaction::verifyEvents;
+ using SimpleRemoveTransaction::defineStates;
+ using SimpleRemoveTransaction::verifyStates;
+ using SimpleRemoveTransaction::readyHandler;
+ using SimpleRemoveTransaction::selectingFwdServerHandler;
+ using SimpleRemoveTransaction::getCurrentServer;
+ using SimpleRemoveTransaction::setDnsUpdateStatus;
+ using SimpleRemoveTransaction::removingFwdRRsHandler;
+ using SimpleRemoveTransaction::selectingRevServerHandler;
+ using SimpleRemoveTransaction::removingRevPtrsHandler;
+ using SimpleRemoveTransaction::processRemoveOkHandler;
+ using SimpleRemoveTransaction::processRemoveFailedHandler;
+ using SimpleRemoveTransaction::buildRemoveFwdRRsRequest;
+ using SimpleRemoveTransaction::buildRemoveRevPtrsRequest;
+};
+
+typedef boost::shared_ptr<SimpleRemoveStub> SimpleRemoveStubPtr;
+
+/// @brief Test fixture for testing SimpleRemoveTransaction
+///
+/// Note this class uses SimpleRemoveStub class to exercise non-public
+/// aspects of SimpleRemoveTransaction.
+class SimpleRemoveTransactionTest : public TransactionTest {
+public:
+ SimpleRemoveTransactionTest() {
+ }
+
+ virtual ~SimpleRemoveTransactionTest() {
+ }
+
+ /// @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
+ SimpleRemoveStubPtr 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 (SimpleRemoveStubPtr(new SimpleRemoveStub(io_service_, ncr_,
+ forward_domain_,
+ reverse_domain_,
+ cfg_mgr_)));
+ }
+
+ /// @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
+ SimpleRemoveStubPtr 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 (SimpleRemoveStubPtr(new SimpleRemoveStub(io_service_, ncr_,
+ forward_domain_,
+ reverse_domain_,
+ cfg_mgr_)));
+ }
+
+ /// @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.
+ SimpleRemoveStubPtr prepHandlerTest(unsigned int state, unsigned int event,
+ unsigned int change_mask = FWD_AND_REV_CHG,
+ short family = AF_INET) {
+ SimpleRemoveStubPtr 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 SimpleRemoveTransaction construction.
+/// This test verifies that:
+/// 1. Construction with invalid type of request
+/// 2. Valid construction functions properly
+TEST(SimpleRemoveTransaction, construction) {
+ asiolink::IOServicePtr io_service(new isc::asiolink::IOService());
+ D2CfgMgrPtr cfg_mgr(new D2CfgMgr());
+
+ 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, "
+ " \"use-conflict-resolution\" : true "
+ "}";
+
+ 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(SimpleRemoveTransaction(io_service, ncr,
+ forward_domain, reverse_domain, cfg_mgr),
+ SimpleRemoveTransactionError);
+
+ // Verify that a valid construction attempt works.
+ ncr->setChangeType(isc::dhcp_ddns::CHG_REMOVE);
+ EXPECT_NO_THROW(SimpleRemoveTransaction(io_service, ncr,
+ forward_domain, reverse_domain,
+ cfg_mgr));
+}
+
+/// @brief Tests event and state dictionary construction and verification.
+TEST_F(SimpleRemoveTransactionTest, dictionaryCheck) {
+ SimpleRemoveStubPtr 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());
+}
+
+/// @brief Tests construction of a DNS update request for removing forward
+/// dns RR entries.
+TEST_F(SimpleRemoveTransactionTest, buildRemoveFwdRRsRequest) {
+ // Create a IPv4 forward replace transaction.
+ // Verify the request builds without error.
+ // and then verify the request contents.
+ SimpleRemoveStubPtr name_remove;
+ ASSERT_NO_THROW(name_remove = makeTransaction4(FORWARD_CHG));
+ ASSERT_NO_THROW(name_remove->buildRemoveFwdRRsRequest());
+ checkSimpleRemoveFwdRRsRequest(*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(FORWARD_CHG));
+ ASSERT_NO_THROW(name_remove->buildRemoveFwdRRsRequest());
+ checkSimpleRemoveFwdRRsRequest(*name_remove);
+}
+
+/// @brief Tests the construction of a DNS update request for removing a
+/// reverse dns entry.
+TEST_F(SimpleRemoveTransactionTest, buildRemoveRevPtrsRequest) {
+ // Create a IPv4 reverse replace transaction.
+ // Verify the request builds without error.
+ // and then verify the request contents.
+ SimpleRemoveStubPtr name_remove;
+ ASSERT_NO_THROW(name_remove = makeTransaction4(REVERSE_CHG));
+ ASSERT_NO_THROW(name_remove->buildRemoveRevPtrsRequest());
+ checkSimpleRemoveRevPtrsRequest(*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(REVERSE_CHG));
+ ASSERT_NO_THROW(name_remove->buildRemoveRevPtrsRequest());
+ checkSimpleRemoveRevPtrsRequest(*name_remove);
+}
+
+// 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(SimpleRemoveTransactionTest, readyHandler) {
+ SimpleRemoveStubPtr 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.
+ CHECK_CONTEXT(name_remove, NameChangeTransaction::SELECTING_FWD_SERVER_ST,
+ NameChangeTransaction::SELECT_SERVER_EVT);
+
+ // 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.
+ CHECK_CONTEXT(name_remove, NameChangeTransaction::SELECTING_FWD_SERVER_ST,
+ NameChangeTransaction::SELECT_SERVER_EVT);
+
+ // 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.
+ CHECK_CONTEXT(name_remove, NameChangeTransaction::SELECTING_REV_SERVER_ST,
+ NameChangeTransaction::SELECT_SERVER_EVT);
+
+ // 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(), SimpleRemoveTransactionError);
+}
+
+
+// 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(SimpleRemoveTransactionTest, selectingFwdServerHandler) {
+ SimpleRemoveStubPtr 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) {
+ SCOPED_TRACE(testing::Message() << " num_servers: " << num_servers
+ << " selections:" << i);
+ // Run selectingFwdServerHandler.
+ ASSERT_NO_THROW(name_remove->selectingFwdServerHandler());
+
+ // Verify that a server was selected.
+ ASSERT_TRUE(name_remove->getCurrentServer());
+
+ // Verify that we transitioned correctly.
+ CHECK_CONTEXT(name_remove, SimpleRemoveTransaction::REMOVING_FWD_RRS_ST,
+ NameChangeTransaction::SERVER_SELECTED_EVT);
+
+ // Post a server IO error event. This simulates an IO error occurring
+ // and a need to select the new server.
+ ASSERT_NO_THROW(name_remove->postNextEvent(NameChangeTransaction::
+ SERVER_IO_ERROR_EVT));
+ }
+
+ // 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());
+ CHECK_CONTEXT(name_remove, NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ NameChangeTransaction::NO_MORE_SERVERS_EVT);
+
+ // 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(),
+ SimpleRemoveTransactionError);
+}
+
+// ************************ removingFwdRRsHandler Tests *****************
+
+// Tests that removingFwdRRsHandler rejects invalid events.
+TEST_F(SimpleRemoveTransactionTest, removingFwdRRsHandler_InvalidEvent) {
+ SimpleRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler but with
+ // an invalid event.
+ ASSERT_NO_THROW(
+ name_remove = prepHandlerTest(SimpleRemoveTransaction::REMOVING_FWD_RRS_ST,
+ StateModel::NOP_EVT)
+ );
+
+ // Running the handler should throw.
+ EXPECT_THROW(name_remove->removingFwdRRsHandler(),
+ SimpleRemoveTransactionError);
+}
+
+// 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(SimpleRemoveTransactionTest, removingFwdRRsHandler_FwdOnlyOK) {
+ SimpleRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(
+ name_remove = prepHandlerTest(SimpleRemoveTransaction::REMOVING_FWD_RRS_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 removingFwdRRsHandler to construct and send the request.
+ EXPECT_NO_THROW(name_remove->removingFwdRRsHandler());
+
+ // Verify that an update message was constructed properly.
+ checkSimpleRemoveFwdRRsRequest(*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.
+ CHECK_CONTEXT(name_remove, SimpleRemoveTransaction::REMOVING_FWD_RRS_ST,
+ NameChangeTransaction::NOP_EVT);
+
+ // 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.
+ CHECK_CONTEXT(name_remove, NameChangeTransaction::PROCESS_TRANS_OK_ST,
+ NameChangeTransaction::UPDATE_OK_EVT);
+}
+
+// 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(SimpleRemoveTransactionTest, removingFwdRRsHandler_OtherRcode) {
+ SimpleRemoveStubPtr name_remove;
+ // Create the transaction.
+ ASSERT_NO_THROW(
+ name_remove = prepHandlerTest(SimpleRemoveTransaction::REMOVING_FWD_RRS_ST,
+ NameChangeTransaction::SERVER_SELECTED_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. Verify that we transitioned
+ // correctly.
+ CHECK_CONTEXT(name_remove, NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ NameChangeTransaction::UPDATE_FAILED_EVT);
+}
+
+// Tests removingFwdRRsHandler 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(SimpleRemoveTransactionTest, removingFwdRRsHandler_Timeout) {
+ SimpleRemoveStubPtr name_remove;
+
+ // Create the transaction.
+ ASSERT_NO_THROW(
+ name_remove = prepHandlerTest(SimpleRemoveTransaction::REMOVING_FWD_RRS_ST,
+ NameChangeTransaction::SERVER_SELECTED_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.
+ CHECK_CONTEXT(name_remove, SimpleRemoveTransaction::REMOVING_FWD_RRS_ST,
+ NameChangeTransaction::SERVER_SELECTED_EVT);
+ } else {
+ // Server retries should be exhausted, time for a new server.
+ CHECK_CONTEXT(name_remove, SimpleRemoveTransaction::SELECTING_FWD_SERVER_ST,
+ NameChangeTransaction::SERVER_IO_ERROR_EVT);
+ }
+ }
+}
+
+// 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(SimpleRemoveTransactionTest, removingFwdRRsHandler_InvalidResponse) {
+ SimpleRemoveStubPtr name_remove;
+
+ // Create the transaction.
+ ASSERT_NO_THROW(
+ name_remove = prepHandlerTest(SimpleRemoveTransaction::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.
+ CHECK_CONTEXT(name_remove, SimpleRemoveTransaction::REMOVING_FWD_RRS_ST,
+ NameChangeTransaction::SERVER_SELECTED_EVT);
+ } else {
+ // Server retries should be exhausted, time for a new server.
+ CHECK_CONTEXT(name_remove, SimpleRemoveTransaction::SELECTING_FWD_SERVER_ST,
+ NameChangeTransaction::SERVER_IO_ERROR_EVT);
+ }
+ }
+}
+
+
+// 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(SimpleRemoveTransactionTest, selectingRevServerHandler) {
+ SimpleRemoveStubPtr 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) {
+ SCOPED_TRACE(testing::Message() << " num_servers: " << num_servers
+ << " selections:" << i);
+ // Run selectingRevServerHandler.
+ ASSERT_NO_THROW(name_remove->selectingRevServerHandler());
+
+ // Verify that a server was selected.
+ ASSERT_TRUE(name_remove->getCurrentServer());
+
+ // Verify that we transitioned correctly.
+ CHECK_CONTEXT(name_remove, SimpleRemoveTransaction::REMOVING_REV_PTRS_ST,
+ NameChangeTransaction::SERVER_SELECTED_EVT);
+
+ // Post a server IO error event. This simulates an IO error occurring
+ // and a need to select the new server.
+ ASSERT_NO_THROW(name_remove->postNextEvent(NameChangeTransaction::
+ SERVER_IO_ERROR_EVT));
+ }
+
+ // 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());
+ CHECK_CONTEXT(name_remove, NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ NameChangeTransaction::NO_MORE_SERVERS_EVT);
+
+ // 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(),
+ SimpleRemoveTransactionError);
+}
+
+//************************** removingRevPtrsHandler tests *****************
+
+// Tests that removingRevPtrsHandler rejects invalid events.
+TEST_F(SimpleRemoveTransactionTest, removingRevPtrsHandler_InvalidEvent) {
+ SimpleRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler but with
+ // an invalid event.
+ ASSERT_NO_THROW(
+ name_remove = prepHandlerTest(SimpleRemoveTransaction::REMOVING_REV_PTRS_ST,
+ StateModel::NOP_EVT)
+ );
+
+ // Running the handler should throw.
+ EXPECT_THROW(name_remove->removingRevPtrsHandler(),
+ SimpleRemoveTransactionError);
+}
+
+// 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 successful update.
+//
+TEST_F(SimpleRemoveTransactionTest, removingRevPtrsHandler_RevOnlyOK) {
+ SimpleRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(
+ name_remove = prepHandlerTest(SimpleRemoveTransaction::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.
+ checkSimpleRemoveRevPtrsRequest(*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.
+ CHECK_CONTEXT(name_remove, SimpleRemoveTransaction::REMOVING_REV_PTRS_ST,
+ StateModel::NOP_EVT);
+
+ // 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.
+ CHECK_CONTEXT(name_remove, NameChangeTransaction::PROCESS_TRANS_OK_ST,
+ NameChangeTransaction::UPDATE_OK_EVT);
+}
+
+// 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 FQDN is NOT in use.
+//
+TEST_F(SimpleRemoveTransactionTest, removingRevPtrsHandler_FqdnNotInUse) {
+ SimpleRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(
+ name_remove = prepHandlerTest(SimpleRemoveTransaction::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.
+ checkSimpleRemoveRevPtrsRequest(*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.
+ CHECK_CONTEXT(name_remove, SimpleRemoveTransaction::REMOVING_REV_PTRS_ST,
+ StateModel::NOP_EVT);
+
+ // Simulate receiving a RRSET does not exist.
+ name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NXRRSET());
+
+ // 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.
+ CHECK_CONTEXT(name_remove, NameChangeTransaction::PROCESS_TRANS_OK_ST,
+ NameChangeTransaction::UPDATE_OK_EVT);
+}
+
+// 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(SimpleRemoveTransactionTest, removingRevPtrsHandler_OtherRcode) {
+ SimpleRemoveStubPtr name_remove;
+ // Create the transaction.
+ ASSERT_NO_THROW(
+ name_remove = prepHandlerTest(SimpleRemoveTransaction::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.
+ CHECK_CONTEXT(name_remove, NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ NameChangeTransaction::UPDATE_FAILED_EVT);
+}
+
+// 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(SimpleRemoveTransactionTest, removingRevPtrsHandler_Timeout) {
+ SimpleRemoveStubPtr name_remove;
+ // Create the transaction.
+ ASSERT_NO_THROW(
+ name_remove = prepHandlerTest(SimpleRemoveTransaction::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.
+ CHECK_CONTEXT(name_remove, SimpleRemoveTransaction::REMOVING_REV_PTRS_ST,
+ NameChangeTransaction::SERVER_SELECTED_EVT);
+ } else {
+ // Server retries should be exhausted, time for a new server.
+ CHECK_CONTEXT(name_remove, NameChangeTransaction::SELECTING_REV_SERVER_ST,
+ NameChangeTransaction::SERVER_IO_ERROR_EVT);
+ }
+ }
+}
+
+
+// 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(SimpleRemoveTransactionTest, removingRevPtrsHandler_CorruptResponse) {
+ SimpleRemoveStubPtr name_remove;
+ // Create the transaction.
+ ASSERT_NO_THROW(
+ name_remove = prepHandlerTest(SimpleRemoveTransaction::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.
+ CHECK_CONTEXT(name_remove, SimpleRemoveTransaction::REMOVING_REV_PTRS_ST,
+ NameChangeTransaction::SERVER_SELECTED_EVT);
+ } else {
+ // Server retries should be exhausted, time for a new server.
+ CHECK_CONTEXT(name_remove, NameChangeTransaction::SELECTING_REV_SERVER_ST,
+ NameChangeTransaction::SERVER_IO_ERROR_EVT);
+ }
+ }
+}
+
+// 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(SimpleRemoveTransactionTest, processRemoveOkHandler) {
+ SimpleRemoveStubPtr 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.
+ CHECK_CONTEXT(name_remove, StateModel::END_ST, StateModel::END_EVT);
+
+ // 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(),
+ SimpleRemoveTransactionError);
+}
+
+// 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(SimpleRemoveTransactionTest, processRemoveFailedHandler) {
+ SimpleRemoveStubPtr 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.)
+ CHECK_CONTEXT(name_remove, StateModel::END_ST, StateModel::END_EVT);
+
+ // 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(),
+ SimpleRemoveTransactionError);
+}
+
+// Tests the processRemoveFailedHandler functionality.
+// It verifies behavior for posted event of NO_MORE_SERVERS_EVT.
+TEST_F(SimpleRemoveTransactionTest, processRemoveFailedHandler_NoMoreServers) {
+ SimpleRemoveStubPtr 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.)
+ CHECK_CONTEXT(name_remove, StateModel::END_ST, StateModel::END_EVT);
+}
+
+// Tests the processRemoveFailedHandler functionality.
+// It verifies behavior for posted event of SERVER_IO_ERROR_EVT.
+TEST_F(SimpleRemoveTransactionTest, processRemoveFailedHandler_ServerIOError) {
+ SimpleRemoveStubPtr 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.)
+ CHECK_CONTEXT(name_remove, StateModel::END_ST, StateModel::END_EVT);
+}
+
+// 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(SimpleRemoveTransactionTest, removingFwdRRsHandler_SendUpdateException) {
+ SimpleRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(
+ name_remove = prepHandlerTest(SimpleRemoveTransaction::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.
+ CHECK_CONTEXT(name_remove, NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ NameChangeTransaction::UPDATE_FAILED_EVT);
+}
+
+// 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(SimpleRemoveTransactionTest, removingRevPtrsHandler_SendUpdateException) {
+ SimpleRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(
+ name_remove = prepHandlerTest(SimpleRemoveTransaction::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.
+ CHECK_CONTEXT(name_remove, NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ NameChangeTransaction::UPDATE_FAILED_EVT);
+}
+
+// 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(SimpleRemoveTransactionTest, removingFwdRRsHandler_BuildRequestException) {
+ SimpleRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(
+ name_remove = prepHandlerTest(SimpleRemoveTransaction::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.
+ CHECK_CONTEXT(name_remove, NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ NameChangeTransaction::UPDATE_FAILED_EVT);
+}
+
+// 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(SimpleRemoveTransactionTest, removingRevPTRsHandler_BuildRequestException) {
+ SimpleRemoveStubPtr name_remove;
+ // Create and prep a transaction, poised to run the handler.
+ ASSERT_NO_THROW(
+ name_remove = prepHandlerTest(SimpleRemoveTransaction::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.
+ CHECK_CONTEXT(name_remove, NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ NameChangeTransaction::UPDATE_FAILED_EVT);
+}
+
+}
}
}
+\"ddns-update-on-renew\" {
+ switch(driver.ctx_) {
+ case isc::dhcp::Parser4Context::DHCP4:
+ case isc::dhcp::Parser4Context::SUBNET4:
+ case isc::dhcp::Parser4Context::SHARED_NETWORK:
+ return isc::dhcp::Dhcp4Parser::make_DDNS_UPDATE_ON_RENEW(driver.loc_);
+ default:
+ return isc::dhcp::Dhcp4Parser::make_STRING("ddns-update-on-renew", driver.loc_);
+ }
+}
+
+\"ddns-use-conflict-resolution\" {
+ switch(driver.ctx_) {
+ case isc::dhcp::Parser4Context::DHCP4:
+ case isc::dhcp::Parser4Context::SUBNET4:
+ case isc::dhcp::Parser4Context::SHARED_NETWORK:
+ return isc::dhcp::Dhcp4Parser::make_DDNS_USE_CONFLICT_RESOLUTION(driver.loc_);
+ default:
+ return isc::dhcp::Dhcp4Parser::make_STRING("ddns-use-conflict-resolution", driver.loc_);
+ }
+}
+
\"subnet4\" {
switch(driver.ctx_) {
case isc::dhcp::Parser4Context::DHCP4:
DDNS_REPLACE_CLIENT_NAME "ddns-replace-client-name"
DDNS_GENERATED_PREFIX "ddns-generated-prefix"
DDNS_QUALIFYING_SUFFIX "ddns-qualifying-suffix"
+ DDNS_UPDATE_ON_RENEW "ddns-update-on-renew"
+ DDNS_USE_CONFLICT_RESOLUTION "ddns-use-conflict-resolution"
STORE_EXTENDED_INFO "store-extended-info"
SUBNET4 "subnet4"
SUBNET_4O6_INTERFACE "4o6-interface"
| ddns_replace_client_name
| ddns_generated_prefix
| ddns_qualifying_suffix
+ | ddns_update_on_renew
+ | ddns_use_conflict_resolution
| store_extended_info
| statistic_default_sample_count
| statistic_default_sample_age
ctx.leave();
};
+ddns_update_on_renew: DDNS_UPDATE_ON_RENEW COLON BOOLEAN {
+ ctx.unique("ddns-update-on-renew", ctx.loc2pos(@1));
+ ElementPtr b(new BoolElement($3, ctx.loc2pos(@3)));
+ ctx.stack_.back()->set("ddns-update-on-renew", b);
+};
+
+ddns_use_conflict_resolution: DDNS_USE_CONFLICT_RESOLUTION COLON BOOLEAN {
+ ctx.unique("ddns-use-conflict-resolution", ctx.loc2pos(@1));
+ ElementPtr b(new BoolElement($3, ctx.loc2pos(@3)));
+ ctx.stack_.back()->set("ddns-use-conflict-resolution", b);
+};
+
hostname_char_set: HOSTNAME_CHAR_SET {
ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
| ddns_replace_client_name
| ddns_generated_prefix
| ddns_qualifying_suffix
+ | ddns_update_on_renew
+ | ddns_use_conflict_resolution
| hostname_char_set
| hostname_char_replacement
| store_extended_info
| ddns_replace_client_name
| ddns_generated_prefix
| ddns_qualifying_suffix
+ | ddns_update_on_renew
+ | ddns_use_conflict_resolution
| hostname_char_set
| hostname_char_replacement
| store_extended_info
void
Dhcpv4Srv::createNameChangeRequests(const Lease4Ptr& lease,
- const Lease4Ptr& old_lease) {
+ const Lease4Ptr& old_lease,
+ const DdnsParams& ddns_params) {
if (!lease) {
isc_throw(isc::Unexpected,
"NULL lease specified when creating NameChangeRequest");
+ }
+
+ // Nothing to do if updates are not enabled.
+ if (!ddns_params.getEnableUpdates()) {
+ return;
+ }
- } else if (!old_lease || !lease->hasIdenticalFqdn(*old_lease)) {
+ if (!old_lease || ddns_params.getUpdateOnRenew() || !lease->hasIdenticalFqdn(*old_lease)) {
if (old_lease) {
// Queue's up a remove of the old lease's DNS (if needed)
queueNCR(CHG_REMOVE, old_lease);
// Set T1 and T2 per configuration.
setTeeTimes(lease, subnet, resp);
- // Create NameChangeRequests if DDNS is enabled and this is a
- // real allocation.
- if (!fake_allocation && (ex.getContext()->getDdnsParams()->getEnableUpdates())) {
+ // Create NameChangeRequests if this is a real allocation.
+ if (!fake_allocation) {
try {
LOG_DEBUG(ddns4_logger, DBG_DHCP4_DETAIL, DHCP4_NCR_CREATE)
.arg(query->getLabel());
- createNameChangeRequests(lease, ctx->old_lease_);
-
+ createNameChangeRequests(lease, ctx->old_lease_,
+ *ex.getContext()->getDdnsParams());
} catch (const Exception& ex) {
LOG_ERROR(ddns4_logger, DHCP4_NCR_CREATION_FAILED)
.arg(query->getLabel())
///
/// @param lease A pointer to the new lease which has been acquired.
/// @param old_lease A pointer to the instance of the old lease which has
+ /// @param ddns_params DDNS configuration parameters
/// been replaced by the new lease passed in the first argument. The NULL
/// value indicates that the new lease has been allocated, rather than
/// lease being renewed.
void createNameChangeRequests(const Lease4Ptr& lease,
- const Lease4Ptr& old_lease);
+ const Lease4Ptr& old_lease,
+ const DdnsParams& ddns_params);
/// @brief Attempts to renew received addresses
///
(config_pair.first == "ddns-replace-client-name") ||
(config_pair.first == "ddns-generated-prefix") ||
(config_pair.first == "ddns-qualifying-suffix") ||
+ (config_pair.first == "ddns-update-on-renew") ||
+ (config_pair.first == "ddns-use-conflict-resolution") ||
(config_pair.first == "store-extended-info") ||
(config_pair.first == "statistic-default-sample-count") ||
(config_pair.first == "statistic-default-sample-age")) {
<< dhcid_id_num << "\" , "
" \"lease-expires-on\" : \"20140121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\" : true "
"}";
return (dhcp_ddns::NameChangeRequest::fromJSON(stream.str()));
: D2ClientConfig::RCM_NEVER);
subnet_->setDdnsGeneratedPrefix("myhost");
subnet_->setDdnsQualifyingSuffix("example.com");
+ subnet_->setDdnsUseConflictResolution(true);
ASSERT_NO_THROW(srv_->startD2());
}
/// @param not_strict_expire_check - when true the comparison of the NCR
/// lease expiration time is conducted as greater than or equal to rather
/// equal to CLTT plus lease length.
+ /// @param exp_use_cr expected value of conflict resolution flag
void verifyNameChangeRequest(const isc::dhcp_ddns::NameChangeType type,
const bool reverse, const bool forward,
const std::string& addr,
const std::string& dhcid,
const time_t cltt,
const uint16_t len,
- const bool not_strict_expire_check = false) {
+ const bool not_strict_expire_check = false,
+ const bool exp_use_cr = true) {
NameChangeRequestPtr ncr;
ASSERT_NO_THROW(ncr = d2_mgr_.peekAt(0));
ASSERT_TRUE(ncr);
}
EXPECT_EQ(len, ncr->getLeaseLength());
EXPECT_EQ(isc::dhcp_ddns::ST_NEW, ncr->getStatus());
+ EXPECT_EQ(exp_use_cr, ncr->useConflictResolution());
// Process the message off the queue
ASSERT_NO_THROW(d2_mgr_.runReadyIO());
true, true);
Lease4Ptr old_lease;
- ASSERT_NO_THROW(srv_->createNameChangeRequests(lease, old_lease));
+ ASSERT_TRUE(getDdnsParams()->getEnableUpdates());
+
+ ASSERT_NO_THROW(srv_->createNameChangeRequests(lease, old_lease, *getDdnsParams()));
ASSERT_EQ(1, d2_mgr_.getQueueSize());
verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
lease->cltt_, 100);
}
-// Test that no NameChangeRequest is generated when a lease is renewed and
-// the FQDN data hasn't changed.
-TEST_F(NameDhcpv4SrvTest, createNameChangeRequestsRenewNoChange) {
+// Verify that conflict resolution is disabled in the NCR when it is
+// disabled for the lease's subnet.
+TEST_F(NameDhcpv4SrvTest, noConflictResolution) {
Lease4Ptr lease = createLease(IOAddress("192.0.2.3"), "myhost.example.com.",
true, true);
+ Lease4Ptr old_lease;
+
+ ASSERT_TRUE(getDdnsParams()->getEnableUpdates());
+ subnet_->setDdnsUseConflictResolution(false);
+
+ ASSERT_NO_THROW(srv_->createNameChangeRequests(lease, old_lease, *getDdnsParams()));
+ ASSERT_EQ(1, d2_mgr_.getQueueSize());
+
+ verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
+ "192.0.2.3", "myhost.example.com.",
+ "00010132E91AA355CFBB753C0F0497A5A940436965"
+ "B68B6D438D98E680BF10B09F3BCF",
+ lease->cltt_, 100, false, false);
+}
+
+
+// Verifies that createNameChange request only generates requests
+// if the situation dictates that it should. It checks:
+//
+// -# enable-updates true or false
+// -# update-on-renew true or false
+// -# Whether or not there is an old lease
+// -# Whether or not the FQDN has changed between old and new lease
+TEST_F(NameDhcpv4SrvTest, createNameChangeRequestsUpdateOnRenew) {
+
+ Lease4Ptr lease1 = createLease(IOAddress("192.0.2.3"), "one.example.com.",
+ true, true);
// Comparison should be case insensitive, so turning some of the
// characters of the old lease hostname to upper case should not
// trigger NCRs.
- Lease4Ptr old_lease = createLease(IOAddress("192.0.2.3"),
- "Myhost.Example.Com.", true, true);
- old_lease->valid_lft_ += 100;
+ Lease4Ptr lease2 = createLease(IOAddress("192.0.2.3"),
+ "two.example.com.", true, true);
+ struct Scenario {
+ std::string description_;
+ bool send_updates_;
+ bool update_on_renew_;
+ Lease4Ptr old_lease_;
+ Lease4Ptr new_lease_;
+ size_t remove_;
+ size_t add_;
+ };
- ASSERT_NO_THROW(srv_->createNameChangeRequests(lease, old_lease));
- ASSERT_EQ(0, d2_mgr_.getQueueSize());
+ // Mnemonic constants.
+ const bool send_updates = true;
+ const bool update_on_renew = true;
+ const size_t remove = 1;
+ const size_t add = 1;
+
+ const std::vector<Scenario> scenarios = {
+ {
+ "#1 update-on-renew false, no old lease",
+ send_updates, !update_on_renew, Lease4Ptr(), lease1, !remove, add
+ },
+ {
+ "#2 update-on-renew false, no change in fqdn",
+ send_updates, !update_on_renew, lease1, lease1, !remove, !add
+ },
+ {
+ "#3 update-on-renew is false, change in fqdn",
+ send_updates, !update_on_renew, lease1, lease2, remove, add
+ },
+ {
+ "#4 update-on-renew is true, no old lease",
+ send_updates, update_on_renew, Lease4Ptr(), lease1, !remove, add
+ },
+ {
+ "#5 update-on-renew is true, no change in fqdn",
+ send_updates, update_on_renew, lease1, lease1, remove, add
+ },
+ {
+ "#6 update-on-renew is true, change in fqdn",
+ send_updates, update_on_renew, lease1, lease2, remove, add
+ },
+ // All prior scenarios test with send-updates true. We really
+ // only need one with it false.
+ {
+ "#7 send-updates false, update-on-renew is true, change in fqdn",
+ !send_updates, update_on_renew, lease1, lease2, !remove, !add
+ }
+ };
+
+ // Iterate over test scenarios.
+ for (auto scenario : scenarios) {
+ SCOPED_TRACE(scenario.description_); {
+ // Set and verify DDNS params flags
+ subnet_->setDdnsSendUpdates(scenario.send_updates_);
+ subnet_->setDdnsUpdateOnRenew(scenario.update_on_renew_);
+
+ ASSERT_EQ(scenario.send_updates_, getDdnsParams()->getEnableUpdates());
+ ASSERT_EQ(scenario.update_on_renew_, getDdnsParams()->getUpdateOnRenew());
+
+ // Call createNameChangeRequests()
+ ASSERT_NO_THROW(srv_->createNameChangeRequests(scenario.new_lease_,
+ scenario.old_lease_,
+ *getDdnsParams()));
+ // Verify queue count is correct.
+ ASSERT_EQ((scenario.remove_ + scenario.add_), d2_mgr_.getQueueSize());
+
+ // If we expect a remove, check it.
+ if (scenario.remove_ > 0) {
+ // Verify NCR content
+ verifyNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE, true, true,
+ scenario.old_lease_->addr_.toText(),
+ scenario.old_lease_->hostname_, "", time(NULL),
+ scenario.old_lease_->valid_lft_, true);
+ }
+
+ // If we expect an add, check it.
+ if (scenario.add_ > 0) {
+ // Verify NCR content
+ verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
+ scenario.new_lease_->addr_.toText(),
+ scenario.new_lease_->hostname_, "", time(NULL),
+ scenario.new_lease_->valid_lft_, true);
+ }
+ }
+ }
}
// Test that the OFFER message generated as a result of the DISCOVER message
#include <dhcp4/json_config_parser.h>
#include <dhcp4/tests/dhcp4_test_utils.h>
#include <dhcp4/tests/get_config_unittest.h>
+#include <testutils/gtest_utils.h>
#include <boost/algorithm/string.hpp>
#include <gtest/gtest.h>
///@{
/// @brief extracted configurations
const char* EXTRACTED_CONFIGS[] = {
+/// put this after const char* EXTRACTED_CONFIGS[] = {
// CONFIGURATION 0
"{\n"
" \"interfaces-config\": {\n"
/// @brief unparsed configurations
const char* UNPARSED_CONFIGS[] = {
+///put this after const char* UNPARSED_CONFIGS[] = {
// CONFIGURATION 0
"{\n"
" \"authoritative\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": true,\n"
" \"ddns-qualifying-suffix\": \"test.suffix.\",\n"
" \"ddns-replace-client-name\": \"when-present\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": true,\n"
" \"ddns-qualifying-suffix\": \"global.suffix.\",\n"
" \"ddns-replace-client-name\": \"always\",\n"
" \"ddns-send-updates\": false,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": true,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 12345,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
std::cerr << ",\n";
}
std::cerr << " // CONFIGURATION " << config_counter;
- ASSERT_NO_THROW(expected = prettyPrint(dhcp));
- ASSERT_NO_THROW(outputFormatted(dhcp->str()));
+ ASSERT_NO_THROW_LOG(expected = prettyPrint(dhcp));
+ ASSERT_NO_THROW_LOG(outputFormatted(dhcp->str()));
} else {
expected = UNPARSED_CONFIGS[config_counter];
// get the expected config using the dhcpv4 syntax parser
ElementPtr jsond;
- ASSERT_NO_THROW(jsond = parseDHCP4(expected, true));
+ ASSERT_NO_THROW_LOG(jsond = parseDHCP4(expected, true));
ElementPtr jsonj;
// get the expected config using the generic JSON syntax parser
- ASSERT_NO_THROW(jsonj = parseJSON(expected));
+ ASSERT_NO_THROW_LOG(jsonj = parseJSON(expected));
// the generic JSON parser does not handle comments
EXPECT_TRUE(isEquivalent(jsond, moveComments(jsonj)));
// check that unparsed and expected values match
// is it a fixed point?
ConstSrvConfigPtr extracted2 = CfgMgr::instance().getStagingCfg();
ConstElementPtr unparsed2;
- ASSERT_NO_THROW(unparsed2 = extracted2->toElement());
+ ASSERT_NO_THROW_LOG(unparsed2 = extracted2->toElement());
ASSERT_TRUE(unparsed2);
EXPECT_TRUE(isEquivalent(unparsed, unparsed2));
}
#include <dhcp4/json_config_parser.h>
#include <dhcp4/tests/dhcp4_test_utils.h>
#include <dhcp4/tests/get_config_unittest.h>
+#include <testutils/gtest_utils.h>
#include <boost/algorithm/string.hpp>
#include <gtest/gtest.h>
std::cerr << ",\n";
}
std::cerr << " // CONFIGURATION " << config_counter;
- ASSERT_NO_THROW(expected = prettyPrint(dhcp));
- ASSERT_NO_THROW(outputFormatted(dhcp->str()));
+ ASSERT_NO_THROW_LOG(expected = prettyPrint(dhcp));
+ ASSERT_NO_THROW_LOG(outputFormatted(dhcp->str()));
} else {
expected = UNPARSED_CONFIGS[config_counter];
// get the expected config using the dhcpv4 syntax parser
ElementPtr jsond;
- ASSERT_NO_THROW(jsond = parseDHCP4(expected, true));
+ ASSERT_NO_THROW_LOG(jsond = parseDHCP4(expected, true));
ElementPtr jsonj;
// get the expected config using the generic JSON syntax parser
- ASSERT_NO_THROW(jsonj = parseJSON(expected));
+ ASSERT_NO_THROW_LOG(jsonj = parseJSON(expected));
// the generic JSON parser does not handle comments
EXPECT_TRUE(isEquivalent(jsond, moveComments(jsonj)));
// check that unparsed and expected values match
// is it a fixed point?
ConstSrvConfigPtr extracted2 = CfgMgr::instance().getStagingCfg();
ConstElementPtr unparsed2;
- ASSERT_NO_THROW(unparsed2 = extracted2->toElement());
+ ASSERT_NO_THROW_LOG(unparsed2 = extracted2->toElement());
ASSERT_TRUE(unparsed2);
EXPECT_TRUE(isEquivalent(unparsed, unparsed2));
}
}
}
+\"ddns-update-on-renew\" {
+ switch(driver.ctx_) {
+ case isc::dhcp::Parser6Context::DHCP6:
+ case isc::dhcp::Parser6Context::SUBNET6:
+ case isc::dhcp::Parser6Context::SHARED_NETWORK:
+ return isc::dhcp::Dhcp6Parser::make_DDNS_UPDATE_ON_RENEW(driver.loc_);
+ default:
+ return isc::dhcp::Dhcp6Parser::make_STRING("ddns-update-on-renew", driver.loc_);
+ }
+}
+
+\"ddns-use-conflict-resolution\" {
+ switch(driver.ctx_) {
+ case isc::dhcp::Parser6Context::DHCP6:
+ case isc::dhcp::Parser6Context::SUBNET6:
+ case isc::dhcp::Parser6Context::SHARED_NETWORK:
+ return isc::dhcp::Dhcp6Parser::make_DDNS_USE_CONFLICT_RESOLUTION(driver.loc_);
+ default:
+ return isc::dhcp::Dhcp6Parser::make_STRING("ddns-use-conflict-resolution", driver.loc_);
+ }
+}
+
+
\"subnet6\" {
switch(driver.ctx_) {
case isc::dhcp::Parser6Context::DHCP6:
DDNS_REPLACE_CLIENT_NAME "ddns-replace-client-name"
DDNS_GENERATED_PREFIX "ddns-generated-prefix"
DDNS_QUALIFYING_SUFFIX "ddns-qualifying-suffix"
+ DDNS_UPDATE_ON_RENEW "ddns-update-on-renew"
+ DDNS_USE_CONFLICT_RESOLUTION "ddns-use-conflict-resolution"
STORE_EXTENDED_INFO "store-extended-info"
SUBNET6 "subnet6"
OPTION_DEF "option-def"
| ddns_replace_client_name
| ddns_generated_prefix
| ddns_qualifying_suffix
+ | ddns_update_on_renew
+ | ddns_use_conflict_resolution
| store_extended_info
| statistic_default_sample_count
| statistic_default_sample_age
ctx.leave();
};
+ddns_update_on_renew: DDNS_UPDATE_ON_RENEW COLON BOOLEAN {
+ ctx.unique("ddns-update-on-renew", ctx.loc2pos(@1));
+ ElementPtr b(new BoolElement($3, ctx.loc2pos(@3)));
+ ctx.stack_.back()->set("ddns-update-on-renew", b);
+};
+
+ddns_use_conflict_resolution: DDNS_USE_CONFLICT_RESOLUTION COLON BOOLEAN {
+ ctx.unique("ddns-use-conflict-resolution", ctx.loc2pos(@1));
+ ElementPtr b(new BoolElement($3, ctx.loc2pos(@3)));
+ ctx.stack_.back()->set("ddns-use-conflict-resolution", b);
+};
+
hostname_char_set: HOSTNAME_CHAR_SET {
ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
| ddns_replace_client_name
| ddns_generated_prefix
| ddns_qualifying_suffix
+ | ddns_update_on_renew
+ | ddns_use_conflict_resolution
| store_extended_info
| unknown_map_entry
;
| ddns_replace_client_name
| ddns_generated_prefix
| ddns_qualifying_suffix
+ | ddns_update_on_renew
+ | ddns_use_conflict_resolution
| store_extended_info
| unknown_map_entry
;
l != ctx.currentIA().changed_leases_.end(); ++l) {
if ((*l)->addr_ == iaaddr->getAddress()) {
- // The address is the same so this must be renewal?
- if ((*l)->hostname_ == opt_fqdn->getDomainName() &&
- (*l)->fqdn_fwd_ == do_fwd && (*l)->fqdn_rev_ == do_rev) {
- // The FQDN is the same, it must be an extension only.
- // @todo - If we decide to allow updates on renews, we
- // will need to bypass this.
+ // The address is the same so this must be renewal. If we're not
+ // always updating on renew, then we only renew if DNS info has
+ // changed.
+ if (!ctx.getDdnsParams()->getUpdateOnRenew() &&
+ ((*l)->hostname_ == opt_fqdn->getDomainName() &&
+ (*l)->fqdn_fwd_ == do_fwd && (*l)->fqdn_rev_ == do_rev)) {
extended_only = true;
} else {
- // The FQDN has changed, queue a CHG_REMOVE of the old data.
+ // Queue a CHG_REMOVE of the old data.
// NCR will only be created if the lease hostname is not
// empty and at least one of the direction flags is true
queueNCR(CHG_REMOVE, *l);
do_fwd, do_rev,
opt_fqdn->getDomainName(),
iaaddr->getAddress().toText(),
- dhcid, 0, iaaddr->getValid()));
-
+ dhcid, 0, iaaddr->getValid(),
+ ctx.getDdnsParams()->getUseConflictResolution()));
LOG_DEBUG(ddns6_logger, DBG_DHCP6_DETAIL,
DHCP6_DDNS_CREATE_ADD_NAME_CHANGE_REQUEST).arg(ncr->toText());
(config_pair.first == "ddns-replace-client-name") ||
(config_pair.first == "ddns-generated-prefix") ||
(config_pair.first == "ddns-qualifying-suffix") ||
+ (config_pair.first == "ddns-update-on-renew") ||
+ (config_pair.first == "ddns-use-conflict-resolution") ||
(config_pair.first == "store-extended-info") ||
(config_pair.first == "statistic-default-sample-count") ||
(config_pair.first == "statistic-default-sample-age")) {
<< dhcid_id_num << "\" , "
" \"lease-expires-on\" : \"20140121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\" : true "
"}";
return (dhcp_ddns::NameChangeRequest::fromJSON(stream.str()));
subnet_->setDdnsQualifyingSuffix("example.com");
subnet_->setHostnameCharSet("[^A-Za-z0-9-]");
subnet_->setHostnameCharReplacement("x");
+ subnet_->setDdnsUseConflictResolution(true);
ASSERT_NO_THROW(srv_->startD2());
}
/// NameChangeRequest.
/// @param fqdn The expected string value of the FQDN, if blank the
/// check is skipped
+ /// @param exp_use_cr expected value of NCR::conflict_resolution_
void verifyNameChangeRequest(const isc::dhcp_ddns::NameChangeType type,
const bool reverse, const bool forward,
const std::string& addr,
const std::string& dhcid,
const uint64_t expires,
const uint16_t len,
- const std::string& fqdn="") {
+ const std::string& fqdn = "",
+ const bool exp_use_cr = true) {
NameChangeRequestPtr ncr;
ASSERT_NO_THROW(ncr = d2_mgr_.peekAt(0));
ASSERT_TRUE(ncr);
EXPECT_EQ(dhcid, ncr->getDhcid().toStr());
}
- EXPECT_EQ(expires, ncr->getLeaseExpiresOn());
+ if (expires != 0) {
+ EXPECT_EQ(expires, ncr->getLeaseExpiresOn());
+ }
+
EXPECT_EQ(len, ncr->getLeaseLength());
EXPECT_EQ(isc::dhcp_ddns::ST_NEW, ncr->getStatus());
EXPECT_EQ(fqdn, ncr->getFqdn());
}
+ EXPECT_EQ(exp_use_cr, ncr->useConflictResolution());
// Process the message off the queue
ASSERT_NO_THROW(d2_mgr_.runReadyIO());
0, 500);
}
+// Verify that conflict resolution is turned off when the
+// subnet has it disabled.
+TEST_F(FqdnDhcpv6SrvTest, noConflictResolution) {
+ // Create Reply message with Client Id and Server id.
+ Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY);
+
+ // Create three IAs, each having different address.
+ addIA(1234, IOAddress("2001:db8:1::1"), answer);
+
+ // Use domain name in upper case. It should be converted to lower-case
+ // before DHCID is calculated. So, we should get the same result as if
+ // we typed domain name in lower-case.
+ Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
+ "MYHOST.EXAMPLE.COM",
+ Option6ClientFqdn::FULL);
+ answer->addOption(fqdn);
+
+ // Create NameChangeRequest for the first allocated address.
+ AllocEngine::ClientContext6 ctx;
+ subnet_->setDdnsUseConflictResolution(false);
+ ctx.subnet_ = subnet_;
+ ctx.fwd_dns_update_ = ctx.rev_dns_update_ = true;
+ ASSERT_NO_THROW(srv_->createNameChangeRequests(answer, ctx));
+ ASSERT_EQ(1, d2_mgr_.getQueueSize());
+
+ // Verify that NameChangeRequest is correct.
+ verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
+ "2001:db8:1::1",
+ "000201415AA33D1187D148275136FA30300478"
+ "FAAAA3EBD29826B5C907B2C9268A6F52",
+ 0, 500, "", false);
+}
+
// Checks that NameChangeRequests to add entries are not
// created when ddns updates are disabled.
TEST_F(FqdnDhcpv6SrvTest, noAddRequestsWhenDisabled) {
}
+// Verifies that the DDNS parameters used for a lease in subnet in
+// a shared-network belong to lease's subnet. This checks that we
+// get the right results even when the allocation engine changes the
+// subnet chosen. The configuration is two 1-address pool subnets in
+// a shared-network. The first will do a SARR, which consumes the first
+// pool. This should cause the allocation engine to dynamically select
+// the second subnet for the second client. The subnets define different
+// values for qualifying suffixes, thus making it simple to verify
+// the appropriate subnet parameters are used. Both clients then
+// renew their leases.
+TEST_F(FqdnDhcpv6SrvTest, ddnsSharedNetworkTest) {
+ std::string config_str =
+ "{ \"interfaces-config\": { \n"
+ " \"interfaces\": [ \"*\" ] \n"
+ "}, \n"
+ "\"preferred-lifetime\": 3000, \n"
+ "\"rebind-timer\": 2000, \n"
+ "\"renew-timer\": 1000, \n"
+ "\"valid-lifetime\": 4000, \n"
+ "\"shared-networks\": [ \n"
+ "{ \n"
+ "\"name\": \"frog\", \n"
+ "\"interface\": \"eth0\", \n"
+ "\"subnet6\": [ { \n"
+ "\"subnet\": \"2001:db8:1::/64\", \n"
+ "\"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::1\" } ], \n"
+ "\"interface\": \"eth0\", \n"
+ "\"ddns-qualifying-suffix\": \"one.example.com.\" \n"
+ " }, \n"
+ " { \n"
+ "\"subnet\": \"2001:db8:2::/64\", \n"
+ "\"pools\": [ { \"pool\": \"2001:db8:2::1 - 2001:db8:2::1\" } ], \n"
+ "\"interface\": \"eth0\", \n"
+ "\"ddns-qualifying-suffix\": \"two.example.com.\" \n"
+ " } ] \n"
+ "} ], \n"
+ "\"ddns-send-updates\": true, \n"
+ "\"dhcp-ddns\" : { \n"
+ " \"enable-updates\" : true \n"
+ " } \n"
+ "}";
+
+ Dhcp6Client client1;
+ client1.setInterface("eth0");
+ client1.requestAddress();
+
+ // Load a configuration with D2 enabled
+ ASSERT_NO_FATAL_FAILURE(configure(config_str, *client1.getServer()));
+ ASSERT_TRUE(CfgMgr::instance().ddnsEnabled());
+ ASSERT_NO_THROW(client1.getServer()->startD2());
+
+ // Include the Client FQDN option.
+ ASSERT_NO_THROW(client1.useFQDN(Option6ClientFqdn::FLAG_S, "client1",
+ Option6ClientFqdn::PARTIAL));
+
+ // Now do a SARR.
+ ASSERT_NO_THROW(client1.doSARR());
+ Pkt6Ptr resp = client1.getContext().response_;
+ ASSERT_TRUE(resp);
+ ASSERT_EQ(DHCPV6_REPLY, static_cast<int>(resp->getType()));
+
+ // Check that the response FQDN is as expected.
+ Option6ClientFqdnPtr fqdn;
+ fqdn = boost::dynamic_pointer_cast<Option6ClientFqdn>(resp->getOption(D6O_CLIENT_FQDN));
+ ASSERT_TRUE(fqdn);
+ EXPECT_EQ("client1.one.example.com.", fqdn->getDomainName());
+
+ // ddns-send-updates for subnet 1 are enabled, verify the NCR is correct.
+ ASSERT_EQ(1, CfgMgr::instance().getD2ClientMgr().getQueueSize());
+ verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true, "2001:db8:1::1",
+ "", 0, 4000, "client1.one.example.com.");
+
+ // Make sure the lease hostname is correct.
+ Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+ IOAddress("2001:db8:1::1"));
+ ASSERT_TRUE(lease);
+ EXPECT_EQ("client1.one.example.com.", lease->hostname_);
+
+ // Now let's try with a different client. Subnet 1 is full so we should get an
+ // address from subnet 2.
+ Dhcp6Client client2(client1.getServer());
+ client2.setInterface("eth0");
+ client2.requestAddress();
+
+ // Include the Client FQDN option.
+ ASSERT_NO_THROW(client2.useFQDN(Option6ClientFqdn::FLAG_S, "client2",
+ Option6ClientFqdn::PARTIAL));
+
+ // Do a SARR.
+ ASSERT_NO_THROW(client2.doSARR());
+ resp = client2.getContext().response_;
+ ASSERT_TRUE(resp);
+ ASSERT_EQ(DHCPV6_REPLY, static_cast<int>(resp->getType()));
+
+ // Check that the response FQDN is as expected.
+ fqdn = boost::dynamic_pointer_cast<Option6ClientFqdn>(resp->getOption(D6O_CLIENT_FQDN));
+ ASSERT_TRUE(fqdn);
+ EXPECT_EQ("client2.two.example.com.", fqdn->getDomainName());
+
+ // ddns-send-updates for subnet 2 are enabled, verify the NCR is correct.
+ ASSERT_EQ(1, CfgMgr::instance().getD2ClientMgr().getQueueSize());
+ verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true, "2001:db8:2::1",
+ "", 0, 4000, "client2.two.example.com.");
+
+ // Make sure the lease hostname is correct.
+ lease = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, IOAddress("2001:db8:2::1"));
+ ASSERT_TRUE(lease);
+ EXPECT_EQ("client2.two.example.com.", lease->hostname_);
+
+ // Now let's check Renewals
+ // First we'll renew a client2.
+ ASSERT_NO_THROW(client2.doRenew());
+ resp = client2.getContext().response_;
+ ASSERT_TRUE(resp);
+ ASSERT_EQ(DHCPV6_REPLY, static_cast<int>(resp->getType()));
+
+ // Check that the response FQDN is as expected.
+ fqdn = boost::dynamic_pointer_cast<Option6ClientFqdn>(resp->getOption(D6O_CLIENT_FQDN));
+ ASSERT_TRUE(fqdn);
+ EXPECT_EQ("client2.two.example.com.", fqdn->getDomainName());
+
+ // ddns-send-updates for subnet 2 are enabled, but currently a renew/rebind does
+ // not update, unless the FQDN or flags change.
+ ASSERT_EQ(0, CfgMgr::instance().getD2ClientMgr().getQueueSize());
+
+ // Make sure the lease hostname is still correct.
+ lease = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, IOAddress("2001:db8:2::1"));
+ ASSERT_TRUE(lease);
+ EXPECT_EQ("client2.two.example.com.", lease->hostname_);
+
+ // Next we'll renew client1
+ ASSERT_NO_THROW(client1.doRenew());
+ resp = client1.getContext().response_;
+ ASSERT_TRUE(resp);
+ ASSERT_EQ(DHCPV6_REPLY, static_cast<int>(resp->getType()));
+
+ // Check that the response FQDN is as expected.
+ fqdn = boost::dynamic_pointer_cast<Option6ClientFqdn>(resp->getOption(D6O_CLIENT_FQDN));
+ ASSERT_TRUE(fqdn);
+ EXPECT_EQ("client1.one.example.com.", fqdn->getDomainName());
+
+ // ddns-send-updates for subnet 1 are enabled, but currently a renew/rebind does
+ // not update, unless the FQDN or flags change.
+ ASSERT_EQ(0, CfgMgr::instance().getD2ClientMgr().getQueueSize());
+
+ // Make sure the lease hostname is correct.
+ lease = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"));
+ ASSERT_TRUE(lease);
+ EXPECT_EQ("client1.one.example.com.", lease->hostname_);
+}
+
+// Verifies that renews only generate NCRs if the situation dictates
+// that it should. It checks:
+//
+// -# enable-updates true or false
+// -# update-on-renew true or false
+// -# Whether or not the FQDN has changed between old and new lease
+TEST_F(FqdnDhcpv6SrvTest, processRequestRenew) {
+ std::string fqdn1 = "one.example.com.";
+ std::string fqdn2 = "two.example.com.";
+ struct Scenario {
+ std::string description_;
+ bool send_updates_;
+ bool update_on_renew_;
+ std::string old_fqdn_;
+ std::string new_fqdn_;
+ size_t remove_;
+ size_t add_;
+ };
+
+ // Mnemonic constants.
+ const bool send_updates = true;
+ const bool update_on_renew = true;
+ const size_t remove = 1;
+ const size_t add = 1;
+
+ const std::vector<Scenario> scenarios = {
+ {
+ "#1 update-on-renew false, no change in fqdn",
+ send_updates, !update_on_renew, fqdn1, fqdn1, !remove, !add
+ },
+ {
+ "#2 update-on-renew is false, change in fqdn",
+ send_updates, !update_on_renew, fqdn1, fqdn2, remove, add
+ },
+ {
+ "#3 update-on-renew is true, no change in fqdn",
+ send_updates, update_on_renew, fqdn1, fqdn1, remove, add
+ },
+ {
+ "#4 update-on-renew is true, change in fqdn",
+ send_updates, update_on_renew, fqdn1, fqdn2, remove, add
+ },
+ // All prior scenarios test with send-updates true. We really
+ // only need one with it false.
+ {
+ "#5 send-updates false, update-on-renew is true, change in fqdn",
+ !send_updates, update_on_renew, fqdn1, fqdn2, !remove, !add
+ }
+ };
+
+ enableD2();
+ subnet_->setDdnsReplaceClientNameMode(D2ClientConfig::RCM_NEVER);
+
+ // Iterate over test scenarios.
+ for (auto scenario : scenarios) {
+ SCOPED_TRACE(scenario.description_); {
+ // Make sure the lease does not exist.
+ ASSERT_FALSE(LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+ IOAddress("2001:db8:1:1::dead:beef")));
+ // Set and verify DDNS params flags
+ subnet_->setDdnsSendUpdates(scenario.send_updates_);
+ subnet_->setDdnsUpdateOnRenew(scenario.update_on_renew_);
+
+ ASSERT_EQ(scenario.send_updates_, getDdnsParams()->getEnableUpdates());
+ ASSERT_EQ(scenario.update_on_renew_, getDdnsParams()->getUpdateOnRenew());
+
+ // Create the "old" lease
+ testProcessMessage(DHCPV6_REQUEST, scenario.old_fqdn_, scenario.old_fqdn_);
+
+ // The lease should have been recorded in the database.
+ Lease6Ptr old_lease = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+ IOAddress("2001:db8:1:1::dead:beef"));
+ ASSERT_TRUE(old_lease);
+
+ if (!scenario.send_updates_ || scenario.old_fqdn_.empty()) {
+ // We should not have an NCR.
+ ASSERT_EQ(0, d2_mgr_.getQueueSize());
+ } else {
+ // We should have an NCR add.
+ ASSERT_EQ(1, d2_mgr_.getQueueSize());
+ verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
+ old_lease->addr_.toText(), "",
+ 0, old_lease->valid_lft_,
+ old_lease->hostname_);
+ }
+
+ // Now let's renew (or create) the lease.
+ testProcessMessage(DHCPV6_RENEW, scenario.new_fqdn_, scenario.new_fqdn_);
+
+ // The lease should have been recorded in the database.
+ Lease6Ptr new_lease = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+ IOAddress("2001:db8:1:1::dead:beef"));
+ ASSERT_TRUE(new_lease);
+
+ // Verify queue count is correct.
+ ASSERT_EQ((scenario.remove_ + scenario.add_), d2_mgr_.getQueueSize());
+
+ // If we expect a remove, check it.
+ if (scenario.remove_ > 0) {
+ // Verify NCR content
+ verifyNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE, true, true,
+ old_lease->addr_.toText(), "",
+ 0, old_lease->valid_lft_,
+ old_lease->hostname_);
+ }
+
+ // If we expect an add, check it.
+ if (scenario.add_ > 0) {
+ // Verify NCR content
+ verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
+ new_lease->addr_.toText(), "",
+ 0, new_lease->valid_lft_,
+ new_lease->hostname_);
+ }
+
+ // Now delete the lease.
+ ASSERT_TRUE(LeaseMgrFactory::instance().deleteLease(new_lease));
+ }
+ }
+}
+
} // end of anonymous namespace
#include <dhcp6/json_config_parser.h>
#include <dhcp6/tests/dhcp6_test_utils.h>
#include <dhcp6/tests/get_config_unittest.h>
+#include <testutils/gtest_utils.h>
#include <boost/algorithm/string.hpp>
#include <gtest/gtest.h>
///@{
/// @brief extracted configurations
const char* EXTRACTED_CONFIGS[] = {
+/// put this after const char* EXTRACTED_CONFIGS[] = {
// CONFIGURATION 0
"{\n"
" \"interfaces-config\": {\n"
/// @brief unparsed configurations
const char* UNPARSED_CONFIGS[] = {
+///put this after const char* UNPARSED_CONFIGS[] = {
// CONFIGURATION 0
"{\n"
" \"calculate-tee-times\": true,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": true,\n"
" \"ddns-qualifying-suffix\": \"test.suffix.\",\n"
" \"ddns-replace-client-name\": \"when-present\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": true,\n"
" \"ddns-qualifying-suffix\": \"global.suffix.\",\n"
" \"ddns-replace-client-name\": \"always\",\n"
" \"ddns-send-updates\": false,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": true,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 12345,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
" \"ddns-qualifying-suffix\": \"\",\n"
" \"ddns-replace-client-name\": \"never\",\n"
" \"ddns-send-updates\": true,\n"
+" \"ddns-update-on-renew\": false,\n"
+" \"ddns-use-conflict-resolution\": true,\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
" \"enable-updates\": false,\n"
// unparse it
ConstSrvConfigPtr extracted = CfgMgr::instance().getStagingCfg();
ConstElementPtr unparsed;
- ASSERT_NO_THROW(unparsed = extracted->toElement());
+ ASSERT_NO_THROW_LOG(unparsed = extracted->toElement());
ConstElementPtr dhcp;
- ASSERT_NO_THROW(dhcp = unparsed->get("Dhcp6"));
+ ASSERT_NO_THROW_LOG(dhcp = unparsed->get("Dhcp6"));
ASSERT_TRUE(dhcp);
// dump if wanted else check
std::cerr << ",\n";
}
std::cerr << " // CONFIGURATION " << config_counter;
- ASSERT_NO_THROW(expected = prettyPrint(dhcp));
- ASSERT_NO_THROW(outputFormatted(dhcp->str()));
+ ASSERT_NO_THROW_LOG(expected = prettyPrint(dhcp));
+ ASSERT_NO_THROW_LOG(outputFormatted(dhcp->str()));
} else {
expected = UNPARSED_CONFIGS[config_counter];
// get the expected config using the dhcpv6 syntax parser
ElementPtr jsond;
- ASSERT_NO_THROW(jsond = parseDHCP6(expected, true));
+ ASSERT_NO_THROW_LOG(jsond = parseDHCP6(expected, true));
// get the expected config using the generic JSON syntax parser
ElementPtr jsonj;
- ASSERT_NO_THROW(jsonj = parseJSON(expected));
+ ASSERT_NO_THROW_LOG(jsonj = parseJSON(expected));
// the generic JSON parser does not handle comments
EXPECT_TRUE(isEquivalent(jsond, moveComments(jsonj)));
// check that unparsed and expected values match
// is it a fixed point?
ConstSrvConfigPtr extracted2 = CfgMgr::instance().getStagingCfg();
ConstElementPtr unparsed2;
- ASSERT_NO_THROW(unparsed2 = extracted2->toElement());
+ ASSERT_NO_THROW_LOG(unparsed2 = extracted2->toElement());
ASSERT_TRUE(unparsed2);
EXPECT_TRUE(isEquivalent(unparsed, unparsed2));
}
#include <dhcp6/json_config_parser.h>
#include <dhcp6/tests/dhcp6_test_utils.h>
#include <dhcp6/tests/get_config_unittest.h>
+#include <testutils/gtest_utils.h>
#include <boost/algorithm/string.hpp>
#include <gtest/gtest.h>
// unparse it
ConstSrvConfigPtr extracted = CfgMgr::instance().getStagingCfg();
ConstElementPtr unparsed;
- ASSERT_NO_THROW(unparsed = extracted->toElement());
+ ASSERT_NO_THROW_LOG(unparsed = extracted->toElement());
ConstElementPtr dhcp;
- ASSERT_NO_THROW(dhcp = unparsed->get("Dhcp6"));
+ ASSERT_NO_THROW_LOG(dhcp = unparsed->get("Dhcp6"));
ASSERT_TRUE(dhcp);
// dump if wanted else check
std::cerr << ",\n";
}
std::cerr << " // CONFIGURATION " << config_counter;
- ASSERT_NO_THROW(expected = prettyPrint(dhcp));
- ASSERT_NO_THROW(outputFormatted(dhcp->str()));
+ ASSERT_NO_THROW_LOG(expected = prettyPrint(dhcp));
+ ASSERT_NO_THROW_LOG(outputFormatted(dhcp->str()));
} else {
expected = UNPARSED_CONFIGS[config_counter];
// get the expected config using the dhcpv6 syntax parser
ElementPtr jsond;
- ASSERT_NO_THROW(jsond = parseDHCP6(expected, true));
+ ASSERT_NO_THROW_LOG(jsond = parseDHCP6(expected, true));
// get the expected config using the generic JSON syntax parser
ElementPtr jsonj;
- ASSERT_NO_THROW(jsonj = parseJSON(expected));
+ ASSERT_NO_THROW_LOG(jsonj = parseJSON(expected));
// the generic JSON parser does not handle comments
EXPECT_TRUE(isEquivalent(jsond, moveComments(jsonj)));
// check that unparsed and expected values match
// is it a fixed point?
ConstSrvConfigPtr extracted2 = CfgMgr::instance().getStagingCfg();
ConstElementPtr unparsed2;
- ASSERT_NO_THROW(unparsed2 = extracted2->toElement());
+ ASSERT_NO_THROW_LOG(unparsed2 = extracted2->toElement());
ASSERT_TRUE(unparsed2);
EXPECT_TRUE(isEquivalent(unparsed, unparsed2));
}
NameChangeRequest::NameChangeRequest()
: change_type_(CHG_ADD), forward_change_(false),
reverse_change_(false), fqdn_(""), ip_io_address_("0.0.0.0"),
- dhcid_(), lease_expires_on_(), lease_length_(0), status_(ST_NEW) {
+ dhcid_(), lease_expires_on_(), lease_length_(0), conflict_resolution_(true),
+ status_(ST_NEW) {
}
NameChangeRequest::NameChangeRequest(const NameChangeType change_type,
const std::string& fqdn, const std::string& ip_address,
const D2Dhcid& dhcid,
const uint64_t lease_expires_on,
- const uint32_t lease_length)
+ const uint32_t lease_length,
+ const bool conflict_resolution)
: change_type_(change_type), forward_change_(forward_change),
reverse_change_(reverse_change), fqdn_(fqdn), ip_io_address_("0.0.0.0"),
dhcid_(dhcid), lease_expires_on_(lease_expires_on),
- lease_length_(lease_length), status_(ST_NEW) {
+ lease_length_(lease_length), conflict_resolution_(conflict_resolution),
+ status_(ST_NEW) {
// User setter to validate fqdn.
setFqdn(fqdn);
// NcrMessageError if the given Element is the wrong type or its data
// content is lexically invalid. If the element is NOT found in the
// map, getElement will throw NcrMessageError indicating the missing
- // member. Currently there are no optional values.
+ // member.
element = ncr->getElement("change-type", element_map);
ncr->setChangeType(element);
element = ncr->getElement("lease-length", element_map);
ncr->setLeaseLength(element);
+ // For backward compatibility use-conflict-resolution is optional
+ // and defaults to true.
+ auto found = element_map.find("use-conflict-resolution");
+ if (found != element_map.end()) {
+ ncr->setConflictResolution(found->second);
+ } else {
+ ncr->setConflictResolution(true);
+ }
+
// All members were in the Element set and were correct lexically. Now
// validate the overall content semantically. This will throw an
// NcrMessageError if anything is amiss.
<< "\"ip-address\":\"" << getIpAddress() << "\","
<< "\"dhcid\":\"" << getDhcid().toStr() << "\","
<< "\"lease-expires-on\":\"" << getLeaseExpiresOnStr() << "\","
- << "\"lease-length\":" << getLeaseLength() << "}";
+ << "\"lease-length\":" << getLeaseLength() << ","
+ << "\"use-conflict-resolution\":"
+ << (useConflictResolution() ? "true" : "false") << "}";
return (stream.str());
}
setLeaseLength(static_cast<uint32_t>(value));
}
+void
+NameChangeRequest::setConflictResolution(const bool value) {
+ conflict_resolution_ = value;
+}
+
+void
+NameChangeRequest::setConflictResolution(isc::data::ConstElementPtr element) {
+ bool value;
+ try {
+ // Get the element's boolean value.
+ value = element->boolValue();
+ } catch (const isc::data::TypeError& ex) {
+ // We expect a boolean Element type, don't have one.
+ isc_throw(NcrMessageError,
+ "Wrong data type for use-conflict-resolution: " << ex.what());
+ }
+
+ // Good to go, make the assignment.
+ setConflictResolution(value);
+}
+
void
NameChangeRequest::setStatus(const NameChangeStatus value) {
status_ = value;
<< "IP Address: [" << ip_io_address_ << "]" << std::endl
<< "DHCID: [" << dhcid_.toStr() << "]" << std::endl
<< "Lease Expires On: " << getLeaseExpiresOnStr() << std::endl
- << "Lease Length: " << lease_length_ << std::endl;
+ << "Lease Length: " << lease_length_ << std::endl
+ << "Conflict Resolution: " << (conflict_resolution_ ? "yes" : "no")
+ << std::endl;
return (stream.str());
}
(ip_io_address_ == other.ip_io_address_) &&
(dhcid_ == other.dhcid_) &&
(lease_expires_on_ == other.lease_expires_on_) &&
- (lease_length_ == other.lease_length_));
+ (lease_length_ == other.lease_length_) &&
+ (conflict_resolution_ == other.conflict_resolution_));
}
bool
-// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
/// expires.
/// @param lease_length the amount of time in seconds for which the
/// lease is valid (TTL).
+ /// @param conflict_resolution indicates whether or not conflict resolution
+ /// (per RFC 4703) is enabled.
NameChangeRequest(const NameChangeType change_type,
const bool forward_change, const bool reverse_change,
const std::string& fqdn, const std::string& ip_address,
const D2Dhcid& dhcid,
const uint64_t lease_expires_on,
- const uint32_t lease_length);
+ const uint32_t lease_length,
+ const bool conflict_resolution = true);
/// @brief Static method for creating a NameChangeRequest from a
/// buffer containing a marshalled request in a given format.
/// "ip-address" : "<address>",
/// "dhcid" : "<hex_string>",
/// "lease-expires-on" : "<yyyymmddHHMMSS>",
- /// "lease-length" : <secs>
+ /// "lease-length" : <secs>,
+ /// "use-conflict-resolution": <boolean>
/// }
/// @endcode
///
/// - SS - seconds of the minute (0-59)
/// - lease-length - the length of the lease in seconds. This is an
/// integer and may range between 1 and 4294967295 (2^32 - 1) inclusive.
+ /// - use-conflict-resolution - when true, follow RFC 4703 which uses
+ /// DHCID records to prohibit multiple clients from updating an FQDN
///
/// Examples:
///
/// "ip-address" : "192.168.2.1" ,
/// "dhcid" : "010203040A7F8E3D" ,
/// "lease-expires-on" : "20130121132405",
- /// "lease-length" : 1300
+ /// "lease-length" : 1300,
+ /// "use-conflict-resolution": true
/// }
/// @endcode
///
/// "ip-address" : "2001::db8:1::2",
/// "dhcid" : "010203040A7F8E3D" , "
/// "lease-expires-on" : "20130121132405",
- /// "lease-length" : 27400
+ /// "lease-length" : 27400,
+ /// "use-conflict-resolution": true
/// }
/// @endcode
///
/// Element
void setLeaseLength(isc::data::ConstElementPtr element);
+ /// @brief Checks if conflict resolution is enabled
+ ///
+ /// @return a true if the conflict resolution is enabled.
+ bool useConflictResolution() const {
+ return (conflict_resolution_);
+ }
+
+ /// @brief Sets the conflict resolution flag to the given value.
+ ///
+ /// @param value contains the new value to assign to the conflict
+ /// resolution flag
+ void setConflictResolution(const bool value);
+
+ /// @brief Sets the conflict resolution flag to the value of the given Element.
+ ///
+ /// @param element is a boolean Element containing the conflict resolution flag
+ /// value.
+ ///
+ /// @throw NcrMessageError if the element is not a boolean
+ /// Element
+ void setConflictResolution(isc::data::ConstElementPtr element);
+
/// @brief Fetches the request status.
///
/// @return the request status as a NameChangeStatus
/// @brief The amount of time in seconds for which the lease is valid (TTL).
uint32_t lease_length_;
+ /// @brief Indicates if conflict resolution is enabled.
+ bool conflict_resolution_;
+
/// @brief The processing status of the request. Used internally.
NameChangeStatus status_;
};
" \"ip-address\" : \"192.168.2.1\" , "
" \"dhcid\" : \"010203040A7F8E3D\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\": true"
"}",
// Valid Remove.
"{"
" \"ip-address\" : \"192.168.2.1\" , "
" \"dhcid\" : \"010203040A7F8E3D\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\": false"
"}",
// Valid Add with IPv6 address
"{"
" \"ip-address\" : \"fe80::2acf:e9ff:fe12:e56f\" , "
" \"dhcid\" : \"010203040A7F8E3D\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\" : true"
"}"
};
-// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
#include <dhcp/hwaddr.h>
#include <util/time_utilities.h>
+#include <testutils/gtest_utils.h>
#include <gtest/gtest.h>
#include <algorithm>
" \"ip-address\" : \"192.168.2.1\" , "
" \"dhcid\" : \"010203040A7F8E3D\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\": true"
"}",
// Valid Remove.
"{"
" \"ip-address\" : \"192.168.2.1\" , "
" \"dhcid\" : \"010203040A7F8E3D\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\": true"
"}",
// Valid Add with IPv6 address
"{"
" \"ip-address\" : \"fe80::2acf:e9ff:fe12:e56f\" , "
" \"dhcid\" : \"010203040A7F8E3D\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\": true"
+ "}",
+ // Missing use-conflict-resolution
+ "{"
+ " \"change-type\" : 0 , "
+ " \"forward-change\" : true , "
+ " \"reverse-change\" : false , "
+ " \"fqdn\" : \"walah.walah.com\" , "
+ " \"ip-address\" : \"192.168.2.1\" , "
+ " \"dhcid\" : \"010203040A7F8E3D\" , "
+ " \"lease-expires-on\" : \"20130121132405\" , "
" \"lease-length\" : 1300 "
"}"
};
" \"ip-address\" : \"192.168.2.1\" , "
" \"dhcid\" : \"010203040A7F8E3D\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\": true"
"}",
// Invalid forward change.
"{"
" \"ip-address\" : \"192.168.2.1\" , "
" \"dhcid\" : \"010203040A7F8E3D\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\": true"
"}",
// Invalid reverse change.
"{"
" \"ip-address\" : \"192.168.2.1\" , "
" \"dhcid\" : \"010203040A7F8E3D\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\": true"
"}",
// Forward and reverse change both false.
"{"
" \"ip-address\" : \"192.168.2.1\" , "
" \"dhcid\" : \"010203040A7F8E3D\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\": true"
"}",
// Blank FQDN
"{"
" \"ip-address\" : \"192.168.2.1\" , "
" \"dhcid\" : \"010203040A7F8E3D\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\": true"
"}",
// Malformed FQDN
"{"
" \"ip-address\" : \"192.168.2.1\" , "
" \"dhcid\" : \"010203040A7F8E3D\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\": true"
"}",
// Bad IP address
"{"
" \"dhcid\" : \"010203040A7F8E3D\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
" \"lease-length\" : 1300 "
+ " \"use-conflict-resolution\": true"
"}",
// Blank DHCID
"{"
" \"ip-address\" : \"192.168.2.1\" , "
" \"dhcid\" : \"\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\": true"
"}",
// Odd number of digits in DHCID
"{"
" \"ip-address\" : \"192.168.2.1\" , "
" \"dhcid\" : \"010203040A7F8E3\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\": true"
"}",
// Text in DHCID
"{"
" \"ip-address\" : \"192.168.2.1\" , "
" \"dhcid\" : \"THIS IS BOGUS!!!\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\": true"
"}",
// Invalid lease expiration string
"{"
" \"ip-address\" : \"192.168.2.1\" , "
" \"dhcid\" : \"010203040A7F8E3D\" , "
" \"lease-expires-on\" : \"Wed Jun 26 13:46:46 EDT 2013\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\": true"
"}",
// Non-integer for lease length.
"{"
" \"ip-address\" : \"192.168.2.1\" , "
" \"dhcid\" : \"010203040A7F8E3D\" , "
" \"lease-expires-on\" : \"20130121132405\" , "
- " \"lease-length\" : \"BOGUS\" "
+ " \"lease-length\" : \"BOGUS\", "
+ " \"use-conflict-resolution\": true"
+ "}",
+ // Invalid use-conflict-resolution
+ "{"
+ " \"change-type\" : 0 , "
+ " \"forward-change\" : true , "
+ " \"reverse-change\" : false , "
+ " \"fqdn\" : \"walah.walah.com\" , "
+ " \"ip-address\" : \"192.168.2.1\" , "
+ " \"dhcid\" : \"010203040A7F8E3D\" , "
+ " \"lease-expires-on\" : \"20130121132405\" , "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\": 777"
"}"
-
};
/// @brief Tests the NameChangeRequest constructors.
"\"ip-address\":\"192.168.2.1\","
"\"dhcid\":\"010203040A7F8E3D\","
"\"lease-expires-on\":\"20130121132405\","
- "\"lease-length\":1300"
+ "\"lease-length\":1300,"
+ "\"use-conflict-resolution\":true"
"}";
// Verify that a NameChangeRequests can be instantiated from the
// a valid JSON rendition.
NameChangeRequestPtr ncr;
- ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(msg_str));
+ ASSERT_NO_THROW_LOG(ncr = NameChangeRequest::fromJSON(msg_str));
ASSERT_TRUE(ncr);
// Verify that the JSON string created by the new request equals the
"\"ip-address\":\"192.168.2.1\","
"\"dhcid\":\"010203040A7F8E3D\","
"\"lease-expires-on\":\"20130121132405\","
- "\"lease-length\":1300"
+ "\"lease-length\":1300,"
+ "\"use-conflict-resolution\":true"
"}";
// Create a request from JSON directly.
ASSERT_EQ(ncrProtocolToString(dhcp_ddns::NCR_TCP), "TCP");
}
+TEST(NameChangeRequestTest, useConflictResolutionParsing) {
+ std::string base_json =
+ "{"
+ " \"change-type\" : 0 , "
+ " \"forward-change\" : true , "
+ " \"reverse-change\" : false , "
+ " \"fqdn\" : \"walah.walah.com\" , "
+ " \"ip-address\" : \"192.168.2.1\" , "
+ " \"dhcid\" : \"010203040A7F8E3D\" , "
+ " \"lease-expires-on\" : \"20130121132405\" , "
+ " \"lease-length\" : 1300 ";
+
+ std::string its_true(base_json + ",\"use-conflict-resolution\": true}");
+ NameChangeRequestPtr ncr;
+ ASSERT_NO_THROW_LOG(ncr = NameChangeRequest::fromJSON(its_true));
+ ASSERT_TRUE(ncr);
+ EXPECT_TRUE(ncr->useConflictResolution());
+
+ std::string its_false(base_json + ",\"use-conflict-resolution\": false}");
+ ASSERT_NO_THROW_LOG(ncr = NameChangeRequest::fromJSON(its_false));
+ ASSERT_TRUE(ncr);
+ EXPECT_FALSE(ncr->useConflictResolution());
+
+ std::string its_missing(base_json + "}");
+ ASSERT_NO_THROW_LOG(ncr = NameChangeRequest::fromJSON(its_true));
+ ASSERT_TRUE(ncr);
+ EXPECT_TRUE(ncr->useConflictResolution());
+}
+
} // end of anonymous namespace
/// identifier. For DHCPv6 it will be a DUID.
/// @param label Client identification information in the textual format.
/// This is used for logging purposes.
+/// @param use_conflict_resolution flag that tells D2 whether or not to
+/// use conflict resolution.
///
/// @tparam LeasePtrType Pointer to a lease.
/// @tparam IdentifierType HW Address, Client Identifier or DUID.
template<typename LeasePtrType, typename IdentifierType>
void queueNCRCommon(const NameChangeType& chg_type, const LeasePtrType& lease,
- const IdentifierType& identifier, const std::string& label) {
+ const IdentifierType& identifier, const std::string& label,
+ const bool use_conflict_resolution = true) {
// Check if there is a need for update.
if (lease->hostname_.empty() || (!lease->fqdn_fwd_ && !lease->fqdn_rev_)
DHCPSRV_QUEUE_NCR_SKIP)
.arg(label)
.arg(lease->addr_.toText());
-
+
return;
}
std::vector<uint8_t> hostname_wire;
OptionDataTypeUtil::writeFqdn(lease->hostname_, hostname_wire, true);
D2Dhcid dhcid = D2Dhcid(identifier, hostname_wire);
-
// Create name change request.
NameChangeRequestPtr ncr
(new NameChangeRequest(chg_type, lease->fqdn_fwd_, lease->fqdn_rev_,
lease->hostname_, lease->addr_.toText(),
dhcid, lease->cltt_ + lease->valid_lft_,
- lease->valid_lft_));
+ lease->valid_lft_, use_conflict_resolution));
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL_DATA, DHCPSRV_QUEUE_NCR)
.arg(label)
void queueNCR(const NameChangeType& chg_type, const Lease4Ptr& lease) {
if (lease) {
+ // Figure out from the lease's subnet if we should use conflict resolution.
+ // If there's no subnet, something hinky is going on so we'll set it true.
+ bool use_cr = true;
+ Subnet4Ptr subnet = CfgMgr::instance().getCurrentCfg()
+ ->getCfgSubnets4()->getSubnet(lease->subnet_id_);
+ if (subnet) {
+ // We should always have subnet.
+ use_cr = subnet->getDdnsUseConflictResolution();
+ }
+
// Client id takes precedence over HW address.
if (lease->client_id_) {
queueNCRCommon(chg_type, lease, lease->client_id_->getClientId(),
- Pkt4::makeLabel(lease->hwaddr_, lease->client_id_));
+ Pkt4::makeLabel(lease->hwaddr_, lease->client_id_), use_cr);
} else {
// Client id is not specified for the lease. Use HW address
// instead.
queueNCRCommon(chg_type, lease, lease->hwaddr_,
- Pkt4::makeLabel(lease->hwaddr_, lease->client_id_));
+ Pkt4::makeLabel(lease->hwaddr_, lease->client_id_), use_cr);
}
}
}
void queueNCR(const NameChangeType& chg_type, const Lease6Ptr& lease) {
// DUID is required to generate NCR.
if (lease && (lease->type_ != Lease::TYPE_PD) && lease->duid_) {
+ // Figure out from the lease's subnet if we should use conflict resolution.
+ // If there's no subnet, something hinky is going on so we'll set it true.
+ bool use_cr = true;
+ Subnet6Ptr subnet = CfgMgr::instance().getCurrentCfg()
+ ->getCfgSubnets6()->getSubnet(lease->subnet_id_);
+ if (subnet) {
+ // We should always have subnet.
+ use_cr = subnet->getDdnsUseConflictResolution();
+ }
+
queueNCRCommon(chg_type, lease, *(lease->duid_),
- Pkt6::makeLabel(lease->duid_, lease->hwaddr_));
+ Pkt6::makeLabel(lease->duid_, lease->hwaddr_), use_cr);
}
}
map->set("store-extended-info", Element::create(store_extended_info_));
}
+ if (!cache_threshold_.unspecified()) {
+ map->set("cache-threshold", Element::create(cache_threshold_));
+ }
+
+ if (!cache_max_age_.unspecified()) {
+ map->set("cache-max-age",
+ Element::create(static_cast<long long>(cache_max_age_)));
+ }
+
+ if (!ddns_update_on_renew_.unspecified()) {
+ map->set("ddns-update-on-renew", Element::create(ddns_update_on_renew_));
+ }
+
+ if (!ddns_use_conflict_resolution_.unspecified()) {
+ map->set("ddns-use-conflict-resolution", Element::create(ddns_use_conflict_resolution_));
+ }
+
return (map);
}
calculate_tee_times_(), t1_percent_(), t2_percent_(),
ddns_send_updates_(), ddns_override_no_update_(), ddns_override_client_update_(),
ddns_replace_client_name_mode_(), ddns_generated_prefix_(), ddns_qualifying_suffix_(),
- hostname_char_set_(), hostname_char_replacement_(), store_extended_info_() {
+ hostname_char_set_(), hostname_char_replacement_(), store_extended_info_(),
+ cache_threshold_(), cache_max_age_(), ddns_update_on_renew_(),
+ ddns_use_conflict_resolution_() {
}
/// @brief Virtual destructor.
util::Optional<std::string>
getIface(const Inheritance& inheritance = Inheritance::ALL) const {
return (getProperty<Network>(&Network::getIface, iface_name_,
- inheritance, "interface"));
+ inheritance));
};
/// @brief Sets information about relay
store_extended_info_ = store_extended_info;
}
+ /// @brief Returns percentage to use as cache threshold.
+ ///
+ /// @param inheritance inheritance mode to be used.
+ util::Optional<double>
+ getCacheThreshold(const Inheritance& inheritance = Inheritance::ALL) const {
+ return (getProperty<Network>(&Network::getCacheThreshold,
+ cache_threshold_,
+ inheritance, "cache-threshold"));
+ }
+
+ /// @brief Sets cache threshold for a network.
+ ///
+ /// @param cache_threshold New cache threshold percentage to use.
+ void setCacheThreshold(const util::Optional<double>& cache_threshold) {
+ cache_threshold_ = cache_threshold;
+ }
+
+ /// @brief Returns value in seconds to use as cache maximum age.
+ ///
+ /// @param inheritance inheritance mode to be used.
+ util::Optional<uint32_t>
+ getCacheMaxAge(const Inheritance& inheritance = Inheritance::ALL) const {
+ return (getProperty<Network>(&Network::getCacheMaxAge, cache_max_age_,
+ inheritance, "cache-max-age"));
+ }
+
+ /// @brief Sets cache max for a network.
+ ///
+ /// @param cache_max_age New cache maximum value in seconds to use.
+ void setCacheMaxAge(const util::Optional<uint32_t>& cache_max_age) {
+ cache_max_age_ = cache_max_age;
+ }
+
+ /// @brief Returns ddns-update-on-renew
+ ///
+ /// @param inheritance inheritance mode to be used.
+ util::Optional<bool>
+ getDdnsUpdateOnRenew(const Inheritance& inheritance = Inheritance::ALL) const {
+ return (getProperty<Network>(&Network::getDdnsUpdateOnRenew,
+ ddns_update_on_renew_,
+ inheritance, "ddns-update-on-renew"));
+ }
+
+ /// @brief Sets new ddns-update-on-renew
+ ///
+ /// @param ddns_update_on_renew New value to use.
+ void setDdnsUpdateOnRenew(const util::Optional<bool>& ddns_update_on_renew) {
+ ddns_update_on_renew_ = ddns_update_on_renew;
+ }
+
+ /// @brief Returns ddns-use-conflict-resolution
+ ///
+ /// @param inheritance inheritance mode to be used.
+ util::Optional<bool>
+ getDdnsUseConflictResolution(const Inheritance& inheritance = Inheritance::ALL) const {
+ return (getProperty<Network>(&Network::getDdnsUseConflictResolution,
+ ddns_use_conflict_resolution_,
+ inheritance, "ddns-use-conflict-resolution"));
+ }
+
+ /// @brief Sets new ddns-use-conflict-resolution
+ ///
+ /// @param ddns_use_conflict_resolution New value to use.
+ void setDdnsUseConflictResolution(const util::Optional<bool>& ddns_use_conflict_resolution) {
+ ddns_use_conflict_resolution_ = ddns_use_conflict_resolution;
+ }
+
/// @brief Unparses network object.
///
/// @return A pointer to unparsed network configuration.
/// on the lease.
util::Optional<bool> store_extended_info_;
+ /// @brief Percentage of the lease lifetime to use as cache threshold.
+ util::Optional<double> cache_threshold_;
+
+ /// @brief Value in seconds to use as cache maximal age.
+ util::Optional<uint32_t> cache_max_age_;
+
+ /// @brief Should Kea perform updates when leases are extended
+ util::Optional<bool> ddns_update_on_renew_;
+
+ /// @brief Used to to tell kea-dhcp-ddns whether or not to use conflict resolution.
+ util::Optional<bool> ddns_use_conflict_resolution_;
+
/// @brief Pointer to another network that this network belongs to.
///
/// The most common case is that this instance is a subnet which belongs
<< "' is not a valid regular expression");
}
}
+
+ if (network_data->contains("ddns-update-on-renew")) {
+ network->setDdnsUpdateOnRenew(getBoolean(network_data, "ddns-update-on-renew"));
+ }
+
+ if (network_data->contains("ddns-use-conflict-resolution")) {
+ network->setDdnsUseConflictResolution(getBoolean(network_data, "ddns-use-conflict-resolution"));
+ }
}
} // end of namespace isc::dhcp
{ "store-extended-info", Element::boolean },
{ "statistic-default-sample-count", Element::integer },
{ "statistic-default-sample-age", Element::integer },
- { "multi-threading", Element::map }
+ { "multi-threading", Element::map },
+ { "cache-threshold", Element::real },
+ { "cache-max-age", Element::integer },
+ { "ip-reservations-unique", Element::boolean },
+ { "ddns-update-on-renew", Element::boolean },
+ { "ddns-use-conflict-resolution", Element::boolean }
};
/// @brief This table defines default global values for DHCPv4
{ "hostname-char-replacement", Element::string, "" },
{ "store-extended-info", Element::boolean, "false" },
{ "statistic-default-sample-count", Element::integer, "20" },
- { "statistic-default-sample-age", Element::integer, "0" }
+ { "statistic-default-sample-age", Element::integer, "0" },
+ { "ip-reservations-unique", Element::boolean, "true" },
+ { "ddns-update-on-renew", Element::boolean, "false" },
+ { "ddns-use-conflict-resolution", Element::boolean, "true" }
};
/// @brief This table defines all option definition parameters.
{ "hostname-char-set", Element::string },
{ "hostname-char-replacement", Element::string },
{ "store-extended-info", Element::boolean },
- { "metadata", Element::map }
+ { "metadata", Element::map },
+ { "cache-threshold", Element::real },
+ { "cache-max-age", Element::integer },
+ { "ddns-update-on-renew", Element::boolean },
+ { "ddns-use-conflict-resolution", Element::boolean }
};
/// @brief This table defines default values for each IPv4 subnet.
"calculate-tee-times",
"t1-percent",
"t2-percent",
- "store-extended-info"
+ "store-extended-info",
+ "cache-threshold",
+ "cache-max-age"
};
/// @brief This table defines all pool parameters.
{ "hostname-char-set", Element::string },
{ "hostname-char-replacement", Element::string },
{ "store-extended-info", Element::boolean },
- { "metadata", Element::map }
+ { "metadata", Element::map },
+ { "cache-threshold", Element::real },
+ { "cache-max-age", Element::integer },
+ { "ddns-update-on-renew", Element::boolean },
+ { "ddns-use-conflict-resolution", Element::boolean }
};
/// @brief This table defines default values for each IPv4 shared network.
{ "store-extended-info", Element::boolean },
{ "statistic-default-sample-count", Element::integer },
{ "statistic-default-sample-age", Element::integer },
- { "multi-threading", Element::map }
+ { "multi-threading", Element::map },
+ { "cache-threshold", Element::real },
+ { "cache-max-age", Element::integer },
+ { "ip-reservations-unique", Element::boolean },
+ { "ddns-update-on-renew", Element::boolean },
+ { "ddns-use-conflict-resolution", Element::boolean }
};
/// @brief This table defines default global values for DHCPv6
{ "hostname-char-replacement", Element::string, "" },
{ "store-extended-info", Element::boolean, "false" },
{ "statistic-default-sample-count", Element::integer, "20" },
- { "statistic-default-sample-age", Element::integer, "0" }
+ { "statistic-default-sample-age", Element::integer, "0" },
+ { "ip-reservations-unique", Element::boolean, "true" },
+ { "ddns-update-on-renew", Element::boolean, "false" },
+ { "ddns-use-conflict-resolution", Element::boolean, "true" }
};
/// @brief This table defines all option definition parameters.
{ "hostname-char-set", Element::string },
{ "hostname-char-replacement", Element::string },
{ "store-extended-info", Element::boolean },
- { "metadata", Element::map }
+ { "metadata", Element::map },
+ { "cache-threshold", Element::real },
+ { "cache-max-age", Element::integer },
+ { "ddns-update-on-renew", Element::boolean },
+ { "ddns-use-conflict-resolution", Element::boolean }
};
/// @brief This table defines default values for each IPv6 subnet.
"calculate-tee-times",
"t1-percent",
"t2-percent",
- "store-extended-info"
+ "store-extended-info",
+ "cache-threshold",
+ "cache-max-age"
};
/// @brief This table defines all pool parameters.
{ "hostname-char-set", Element::string },
{ "hostname-char-replacement", Element::string },
{ "store-extended-info", Element::boolean },
- { "metadata", Element::map }
+ { "metadata", Element::map },
+ { "cache-threshold", Element::real },
+ { "cache-max-age", Element::integer },
+ { "ddns-update-on-renew", Element::boolean },
+ { "ddns-use-conflict-resolution", Element::boolean }
};
/// @brief This table defines default values for each IPv6 subnet.
return (subnet_->getDdnsOverrideNoUpdate().get());
}
+
bool DdnsParams::getOverrideClientUpdate() const {
if (!subnet_) {
return (false);
return (sanitizer);
}
+bool
+DdnsParams::getUpdateOnRenew() const {
+ if (!subnet_) {
+ return (false);
+ }
+
+ return (subnet_->getDdnsUpdateOnRenew().get());
+}
+
+bool
+DdnsParams::getUseConflictResolution() const {
+ if (!subnet_) {
+ return (true);
+ }
+
+ return (subnet_->getDdnsUseConflictResolution().get());
+}
+
} // namespace dhcp
} // namespace isc
/// @throw BadValue if the compilation fails.
isc::util::str::StringSanitizerPtr getHostnameSanitizer() const;
+ /// @brief Returns whether or not DNS should be updated when leases renew.
+ ///
+ /// If this is true, DNS should always be updated when leases are
+ /// extended (i.e. renewed/rebound) even if the DNS information
+ /// has not changed.
+ ///
+ /// @return True if updates should always be performed.
+ bool getUpdateOnRenew() const;
+
+ /// @brief Returns whether or not keah-dhcp-ddns should use conflict resolution
+ ///
+ /// This value is communicated to D2 via the NCR. When true, D2 should follow
+ /// follow conflict resolution steps described in RFC 4703. If not, it should
+ /// simple add or remove entries.
+ ///
+ /// @return True if conflict resolution should be used.
+ bool getUseConflictResolution() const;
+
+ /// @brief Returns the subnet-id of the subnet associated with these parameters
+ /// @return value of subnet-id (or 0 if no subnet is associated)
+ SubnetID getSubnetId() const {
+ if (subnet_){
+ return subnet_->getID();
+ } else {
+ return 0;
+ }
+ }
+
private:
/// @brief Subnet from which values should be fetched.
SubnetPtr subnet_;
/// @param srv_elem server top level map to alter
static void moveDdnsParams(isc::data::ElementPtr srv_elem);
+ /// @brief Configures the server to allow or disallow specifying multiple
+ /// hosts with the same IP address/subnet.
+ ///
+ /// This setting is applied in @c CfgDbAccess and @c CfgHosts. This function
+ /// should be called when the server is being configured using the configuration
+ /// file, config-set command or via the configuration backend.
+ ///
+ /// @param unique Boolean value indicating if it is allowed (when false)
+ /// or disallowed to specify multiple hosts with the same IP reservation.
+ void setIPReservationsUnique(const bool unique);
+
/// @brief Unparse a configuration object
///
/// @return a pointer to unparsed configuration
" \"ip-address\" : \"192.168.2.1\" , "
" \"dhcid\" : \"010203040A7F8E3D\" , "
" \"lease-expires-on\" : \"20140121132405\" , "
- " \"lease-length\" : 1300 "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\" : true "
"}";
return (dhcp_ddns::NameChangeRequest::fromJSON(ncr_str));
EXPECT_TRUE(subnet->getStoreExtendedInfo().unspecified());
EXPECT_FALSE(subnet->getStoreExtendedInfo().get());
+
+ EXPECT_TRUE(subnet->getDdnsUpdateOnRenew().unspecified());
+ EXPECT_FALSE(subnet->getDdnsUpdateOnRenew().get());
+
+ EXPECT_TRUE(subnet->getDdnsUseConflictResolution().unspecified());
+ EXPECT_FALSE(subnet->getDdnsUseConflictResolution().get());
}
// This test verifies that it is possible to parse an IPv6 subnet for which
EXPECT_TRUE(subnet->getStoreExtendedInfo().unspecified());
EXPECT_FALSE(subnet->getStoreExtendedInfo().get());
+
+ EXPECT_TRUE(subnet->getDdnsUpdateOnRenew().unspecified());
+ EXPECT_FALSE(subnet->getDdnsUpdateOnRenew().get());
+
+ EXPECT_TRUE(subnet->getDdnsUseConflictResolution().unspecified());
+ EXPECT_FALSE(subnet->getDdnsUseConflictResolution().get());
}
// This test verifies that it is possible to parse an IPv4 shared network
ASSERT_TRUE(network);
EXPECT_TRUE(network->hasFetchGlobalsFn());
-
EXPECT_TRUE(network->getIface().unspecified());
EXPECT_TRUE(network->getIface().empty());
EXPECT_TRUE(network->getStoreExtendedInfo().unspecified());
EXPECT_FALSE(network->getStoreExtendedInfo().get());
+
+ EXPECT_TRUE(network->getDdnsUpdateOnRenew().unspecified());
+ EXPECT_FALSE(network->getDdnsUpdateOnRenew().get());
+
+ EXPECT_TRUE(network->getDdnsUseConflictResolution().unspecified());
+ EXPECT_FALSE(network->getDdnsUseConflictResolution().get());
}
// This test verifies that it is possible to parse an IPv6 shared network
EXPECT_TRUE(network->getStoreExtendedInfo().unspecified());
EXPECT_FALSE(network->getStoreExtendedInfo().get());
+
+ EXPECT_TRUE(network->getDdnsUpdateOnRenew().unspecified());
+ EXPECT_FALSE(network->getDdnsUpdateOnRenew().get());
+
+ EXPECT_TRUE(network->getDdnsUseConflictResolution().unspecified());
+ EXPECT_FALSE(network->getDdnsUseConflictResolution().get());
}
// There's no test for ControlSocketParser, as it is tested in the DHCPv4 code
/// @brief Pointer to the lease object used by the tests.
LeasePtrType lease_;
+ /// @brief Pointer to the lease's subnet
+ SubnetPtr subnet_;
+
/// @brief Constructor.
NCRGeneratorTest()
: d2_mgr_(CfgMgr::instance().getD2ClientMgr()), lease_() {
disableD2();
// Base class TearDown.
::testing::Test::TearDown();
+
+ CfgMgr::instance().clear();
}
/// @brief Enables DHCP-DDNS updates.
const std::string& dhcid,
const uint64_t expires,
const uint16_t len,
- const std::string& fqdn="") {
+ const std::string& fqdn="",
+ const bool use_conflict_resolution = true) {
NameChangeRequestPtr ncr;
ASSERT_NO_THROW(ncr = CfgMgr::instance().getD2ClientMgr().peekAt(0));
ASSERT_TRUE(ncr);
EXPECT_EQ(expires, ncr->getLeaseExpiresOn());
EXPECT_EQ(len, ncr->getLeaseLength());
EXPECT_EQ(isc::dhcp_ddns::ST_NEW, ncr->getStatus());
+ EXPECT_EQ(use_conflict_resolution, ncr->useConflictResolution());
if (!fqdn.empty()) {
EXPECT_EQ(fqdn, ncr->getFqdn());
/// @param rev Perform reverse update.
/// @param fqdn Hostname.
/// @param exp_dhcid Expected DHCID.
+ /// @param exp_use_cr expected value of conflict resolution flag
void testNCR(const NameChangeType chg_type, const bool fwd, const bool rev,
- const std::string& fqdn, const std::string exp_dhcid) {
+ const std::string& fqdn, const std::string exp_dhcid,
+ const bool exp_use_cr = true) {
// Queue NCR.
ASSERT_NO_FATAL_FAILURE(sendNCR(chg_type, fwd, rev, fqdn));
// Expecting one NCR be generated.
// Check the details of the NCR.
verifyNameChangeRequest(chg_type, rev, fwd, lease_->addr_.toText(), exp_dhcid,
lease_->cltt_ + lease_->valid_lft_,
- lease_->valid_lft_);
+ lease_->valid_lft_, fqdn, exp_use_cr);
}
/// @brief Test that calling queueNCR for NULL lease doesn't cause
/// @brief Implementation of the method creating DHCPv6 lease instance.
virtual void initLease() {
+ Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 64, 100, 200, 300, 400));
+ // Normally, this would be set via defaults
+ subnet->setDdnsUseConflictResolution(true);
+
+ Pool6Ptr pool(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"),
+ IOAddress("2001:db8:1::200")));
+ subnet->addPool(pool);
+ subnet_ = subnet;
+
+ CfgMgr& cfg_mgr = CfgMgr::instance();
+ cfg_mgr.getStagingCfg()->getCfgSubnets6()->add(subnet);
+ cfg_mgr.commit();
+
lease_.reset(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"),
- duid_, 1234, 501, 502, 1, HWAddrPtr(), 0));
+ duid_, 1234, 501, 502, subnet_->getID(), HWAddrPtr()));
}
};
}
}
+// Verify that conflict resolution is set correctly by v6 queueNCR()
+TEST_F(NCRGenerator6Test, useConflictResolution) {
+ {
+ SCOPED_TRACE("Subnet flag is false");
+ subnet_->setDdnsUseConflictResolution(false);
+ testNCR(CHG_REMOVE, true, true, "MYHOST.example.com.",
+ "000201BE0D7A66F8AB6C4082E7F8B81E2656667A102E3D0ECCEA5E0DD71730F392119A",
+ false);
+ }
+ {
+ SCOPED_TRACE("Subnet flag is true");
+ subnet_->setDdnsUseConflictResolution(true);
+ testNCR(CHG_REMOVE, true, true, "MYHOST.example.com.",
+ "000201BE0D7A66F8AB6C4082E7F8B81E2656667A102E3D0ECCEA5E0DD71730F392119A",
+ true);
+ }
+}
+
/// @brief Test fixture class implementation for DHCPv4.
class NCRGenerator4Test : public NCRGeneratorTest<Lease4Ptr> {
public:
/// @brief Implementation of the method creating DHCPv4 lease instance.
virtual void initLease() {
+
+ Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3));
+ // Normally, this would be set via defaults
+ subnet->setDdnsUseConflictResolution(true);
+
+ Pool4Ptr pool(new Pool4(IOAddress("192.0.2.100"),
+ IOAddress("192.0.2.200")));
+ subnet->addPool(pool);
+
+ CfgMgr& cfg_mgr = CfgMgr::instance();
+ cfg_mgr.getStagingCfg()->getCfgSubnets4()->add(subnet);
+ cfg_mgr.commit();
+
+ subnet_ = subnet;
lease_.reset(new Lease4(IOAddress("192.0.2.1"), hwaddr_, ClientIdPtr(),
- 100, time(NULL), 1));
+ 100, time(NULL), subnet_->getID()));
}
+
};
// Test creation of the NameChangeRequest for both forward and reverse
}
}
+// Verify that conflict resolution is set correctly by v4 queueNCR()
+TEST_F(NCRGenerator4Test, useConflictResolution) {
+ {
+ SCOPED_TRACE("Subnet flag is false");
+ subnet_->setDdnsUseConflictResolution(false);
+ testNCR(CHG_REMOVE, true, true, "MYHOST.example.com.",
+ "000001E356D43E5F0A496D65BCA24D982D646140813E3"
+ "B03AB370BFF46BFA309AE7BFD", false);
+ }
+ {
+ SCOPED_TRACE("Subnet flag is true");
+ subnet_->setDdnsUseConflictResolution(true);
+ testNCR(CHG_REMOVE, true, true, "MYHOST.example.com.",
+ "000001E356D43E5F0A496D65BCA24D982D646140813E3"
+ "B03AB370BFF46BFA309AE7BFD", true);
+ }
+}
+
} // end of anonymous namespace
// network parameters.
TEST_F(NetworkTest, inheritanceSupport4) {
// Set global values for each parameter.
- globals_->set("interface", Element::create("g"));
globals_->set("valid-lifetime", Element::create(80));
globals_->set("renew-timer", Element::create(80));
globals_->set("rebind-timer", Element::create(80));
globals_->set("hostname-char-set", Element::create("gc"));
globals_->set("hostname-char-replacement", Element::create("gr"));
globals_->set("store-extended-info", Element::create(true));
+ globals_->set("cache-threshold", Element::create(.25));
+ globals_->set("cache-max-age", Element::create(20));
+ globals_->set("ddns-update-on-renew", Element::create(true));
+ globals_->set("ddns-use-conflict-resolution", Element::create(true));
// For each parameter for which inheritance is supported run
// the test that checks if the values are inherited properly.
- {
- SCOPED_TRACE("interface");
- testNetworkInheritance<TestNetwork>(&Network::getIface, &Network::setIface,
- "n", "g");
- }
{
SCOPED_TRACE("client_class");
testNetworkInheritance<TestNetwork>(&Network::getClientClass,
testNetworkInheritance<TestNetwork4>(&Network4::getDdnsReplaceClientNameMode,
&Network4::setDdnsReplaceClientNameMode,
D2ClientConfig::RCM_WHEN_PRESENT,
- D2ClientConfig::RCM_ALWAYS);
+ D2ClientConfig::RCM_ALWAYS);
}
{
SCOPED_TRACE("ddns-generated-prefix");
&Network4::setStoreExtendedInfo,
false, true);
}
+ {
+ SCOPED_TRACE("cache-threshold");
+ testNetworkInheritance<TestNetwork4>(&Network::getCacheThreshold,
+ &Network::setCacheThreshold,
+ .1, .25);
+ }
+ {
+ SCOPED_TRACE("cache-max-age");
+ testNetworkInheritance<TestNetwork4>(&Network::getCacheMaxAge,
+ &Network::setCacheMaxAge,
+ 10, 20);
+ }
+ {
+ SCOPED_TRACE("ddns-update-on-renew");
+ testNetworkInheritance<TestNetwork4>(&Network4::getDdnsUpdateOnRenew,
+ &Network4::setDdnsUpdateOnRenew,
+ false, true);
+ }
+ {
+ SCOPED_TRACE("ddns-use-conflict-resolution");
+ testNetworkInheritance<TestNetwork4>(&Network4::getDdnsUseConflictResolution,
+ &Network4::setDdnsUseConflictResolution,
+ false, true);
+ }
}
// This test verifies that the inheritance is supported for DHCPv6
globals_->set("hostname-char-set", Element::create("gc"));
globals_->set("hostname-char-replacement", Element::create("gr"));
globals_->set("store-extended-info", Element::create(true));
+ globals_->set("ddns-update-on-renew", Element::create(true));
+ globals_->set("ddns-use-conflict-resolution", Element::create(true));
// For each parameter for which inheritance is supported run
// the test that checks if the values are inherited properly.
}
{
SCOPED_TRACE("ddns-send-updates");
- testNetworkInheritance<TestNetwork4>(&Network4::getDdnsSendUpdates,
- &Network4::setDdnsSendUpdates,
+ testNetworkInheritance<TestNetwork6>(&Network6::getDdnsSendUpdates,
+ &Network6::setDdnsSendUpdates,
false, true);
}
{
SCOPED_TRACE("ddns-override-no-update");
- testNetworkInheritance<TestNetwork4>(&Network4::getDdnsOverrideNoUpdate,
- &Network4::setDdnsOverrideNoUpdate,
+ testNetworkInheritance<TestNetwork6>(&Network6::getDdnsOverrideNoUpdate,
+ &Network6::setDdnsOverrideNoUpdate,
false, true);
}
{
SCOPED_TRACE("ddns-override-client-update");
- testNetworkInheritance<TestNetwork4>(&Network4::getDdnsOverrideClientUpdate,
- &Network4::setDdnsOverrideClientUpdate,
+ testNetworkInheritance<TestNetwork6>(&Network6::getDdnsOverrideClientUpdate,
+ &Network6::setDdnsOverrideClientUpdate,
false, true);
}
{
SCOPED_TRACE("ddns-replace-client-name");
- testNetworkInheritance<TestNetwork4>(&Network4::getDdnsReplaceClientNameMode,
- &Network4::setDdnsReplaceClientNameMode,
+ testNetworkInheritance<TestNetwork6>(&Network6::getDdnsReplaceClientNameMode,
+ &Network6::setDdnsReplaceClientNameMode,
D2ClientConfig::RCM_WHEN_PRESENT,
- D2ClientConfig::RCM_ALWAYS);
+ D2ClientConfig::RCM_ALWAYS);
}
{
SCOPED_TRACE("ddns-generated-prefix");
- testNetworkInheritance<TestNetwork4>(&Network4::getDdnsGeneratedPrefix,
- &Network4::setDdnsGeneratedPrefix,
+ testNetworkInheritance<TestNetwork6>(&Network6::getDdnsGeneratedPrefix,
+ &Network6::setDdnsGeneratedPrefix,
"np", "gp");
}
{
SCOPED_TRACE("ddns-qualifying-suffix");
- testNetworkInheritance<TestNetwork4>(&Network4::getDdnsQualifyingSuffix,
- &Network4::setDdnsQualifyingSuffix,
+ testNetworkInheritance<TestNetwork6>(&Network6::getDdnsQualifyingSuffix,
+ &Network6::setDdnsQualifyingSuffix,
"ns", "gs");
}
{
}
{
SCOPED_TRACE("store-extended-info");
- testNetworkInheritance<TestNetwork4>(&Network6::getStoreExtendedInfo,
+ testNetworkInheritance<TestNetwork6>(&Network6::getStoreExtendedInfo,
&Network6::setStoreExtendedInfo,
false, true);
}
+ {
+ SCOPED_TRACE("ddns-update-on-renew");
+ testNetworkInheritance<TestNetwork6>(&Network6::getDdnsUpdateOnRenew,
+ &Network6::setDdnsUpdateOnRenew,
+ false, true);
+ }
+ {
+ SCOPED_TRACE("ddns-use-conflict-resolution");
+ testNetworkInheritance<TestNetwork6>(&Network6::getDdnsUseConflictResolution,
+ &Network6::setDdnsUseConflictResolution,
+ false, true);
+ }
// Interface-id requires special type of test.
boost::shared_ptr<TestNetwork6> net_child(new TestNetwork6());
// parent no global value exists.
TEST_F(NetworkTest, getPropertyNoParentNoChild) {
NetworkPtr net_child(new Network());
- EXPECT_TRUE(net_child->getIface().unspecified());
+ EXPECT_TRUE(net_child->getValid().unspecified());
}
// Test that child network returns specified value.
TEST_F(NetworkTest, getPropertyNoParentChild) {
NetworkPtr net_child(new Network());
- net_child->setIface("child_iface");
+ net_child->setValid(12345);
- EXPECT_FALSE(net_child->getIface().unspecified());
- EXPECT_FALSE(net_child->getIface(Network::Inheritance::NONE).unspecified());
- EXPECT_TRUE(net_child->getIface(Network::Inheritance::PARENT_NETWORK).unspecified());
- EXPECT_TRUE(net_child->getIface(Network::Inheritance::GLOBAL).unspecified());
+ EXPECT_FALSE(net_child->getValid().unspecified());
+ EXPECT_FALSE(net_child->getValid(Network::Inheritance::NONE).unspecified());
+ EXPECT_TRUE(net_child->getValid(Network::Inheritance::PARENT_NETWORK).unspecified());
+ EXPECT_TRUE(net_child->getValid(Network::Inheritance::GLOBAL).unspecified());
- EXPECT_EQ("child_iface", net_child->getIface(Network::Inheritance::NONE).get());
- EXPECT_EQ("child_iface", net_child->getIface().get());
+ EXPECT_EQ(12345, net_child->getValid(Network::Inheritance::NONE).get());
+ EXPECT_EQ(12345, net_child->getValid().get());
}
// Test that parent specific value is returned when the value
// is not specified for the child network.
TEST_F(NetworkTest, getPropertyParentNoChild) {
TestNetworkPtr net_child(new TestNetwork());
- EXPECT_TRUE(net_child->getIface().unspecified());
+ EXPECT_TRUE(net_child->getValid().unspecified());
TestNetworkPtr net_parent(new TestNetwork());
- net_parent->setIface("parent_iface");
- EXPECT_EQ("parent_iface", net_parent->getIface().get());
+ net_parent->setValid(23456);
+ EXPECT_EQ(23456, net_parent->getValid().get());
ASSERT_NO_THROW(net_child->setParent(net_parent));
- EXPECT_FALSE(net_child->getIface().unspecified());
- EXPECT_TRUE(net_child->getIface(Network::Inheritance::NONE).unspecified());
- EXPECT_FALSE(net_child->getIface(Network::Inheritance::PARENT_NETWORK).unspecified());
- EXPECT_TRUE(net_child->getIface(Network::Inheritance::GLOBAL).unspecified());
+ EXPECT_FALSE(net_child->getValid().unspecified());
+ EXPECT_TRUE(net_child->getValid(Network::Inheritance::NONE).unspecified());
+ EXPECT_FALSE(net_child->getValid(Network::Inheritance::PARENT_NETWORK).unspecified());
+ EXPECT_TRUE(net_child->getValid(Network::Inheritance::GLOBAL).unspecified());
- EXPECT_EQ("parent_iface", net_child->getIface().get());
+ EXPECT_EQ(23456, net_child->getValid().get());
}
// Test that value specified for the child network takes
// precedence over the value specified for the parent network.
TEST_F(NetworkTest, getPropertyParentChild) {
TestNetworkPtr net_child(new TestNetwork());
- net_child->setIface("child_iface");
- EXPECT_EQ("child_iface", net_child->getIface().get());
+ net_child->setValid(12345);
+ EXPECT_EQ(12345, net_child->getValid().get());
TestNetworkPtr net_parent(new TestNetwork());
- net_parent->setIface("parent_iface");
- EXPECT_EQ("parent_iface", net_parent->getIface().get());
+ net_parent->setValid(23456);
+ EXPECT_EQ(23456, net_parent->getValid().get());
ASSERT_NO_THROW(net_child->setParent(net_parent));
- EXPECT_FALSE(net_child->getIface().unspecified());
- EXPECT_FALSE(net_child->getIface(Network::Inheritance::NONE).unspecified());
- EXPECT_FALSE(net_child->getIface(Network::Inheritance::PARENT_NETWORK).unspecified());
- EXPECT_TRUE(net_child->getIface(Network::Inheritance::GLOBAL).unspecified());
+ EXPECT_FALSE(net_child->getValid().unspecified());
+ EXPECT_FALSE(net_child->getValid(Network::Inheritance::NONE).unspecified());
+ EXPECT_FALSE(net_child->getValid(Network::Inheritance::PARENT_NETWORK).unspecified());
+ EXPECT_TRUE(net_child->getValid(Network::Inheritance::GLOBAL).unspecified());
- EXPECT_EQ("child_iface", net_child->getIface().get());
+ EXPECT_EQ(12345, net_child->getValid().get());
}
// Test that global value is inherited if there is no network
TEST_F(NetworkTest, getPropertyGlobalNoParentNoChild) {
TestNetworkPtr net_child(new TestNetwork());
- globals_->set("interface", Element::create("global_iface"));
+ globals_->set("valid-lifetime", Element::create(34567));
net_child->setFetchGlobalsFn(getFetchGlobalsFn());
- EXPECT_FALSE(net_child->getIface().unspecified());
- EXPECT_TRUE(net_child->getIface(Network::Inheritance::NONE).unspecified());
- EXPECT_TRUE(net_child->getIface(Network::Inheritance::PARENT_NETWORK).unspecified());
- EXPECT_FALSE(net_child->getIface(Network::Inheritance::GLOBAL).unspecified());
+ EXPECT_FALSE(net_child->getValid().unspecified());
+ EXPECT_TRUE(net_child->getValid(Network::Inheritance::NONE).unspecified());
+ EXPECT_TRUE(net_child->getValid(Network::Inheritance::PARENT_NETWORK).unspecified());
+ EXPECT_FALSE(net_child->getValid(Network::Inheritance::GLOBAL).unspecified());
- EXPECT_EQ("global_iface", net_child->getIface().get());
+ EXPECT_EQ(34567, net_child->getValid().get());
}
// Test that getSiaddr() never fails.
" \"hostname-char-set\": \"[^A-Z]\","
" \"hostname-char-replacement\": \"x\","
" \"store-extended-info\": true,"
+ " \"cache-threshold\": 0.123,"
+ " \"cache-max-age\": 123,"
+ " \"ddns-update-on-renew\": true,"
" \"option-data\": ["
" {"
" \"name\": \"domain-name-servers\","
" \"calculate-tee-times\": true,"
" \"t1-percent\": .45,"
" \"t2-percent\": .65,"
- " \"hostname-char-set\": \"\""
+ " \"hostname-char-set\": \"\","
+ " \"cache-threshold\": .20,"
+ " \"cache-max-age\": 50"
" },"
" {"
" \"id\": 2,"
" \"reservation-mode\": \"all\","
" \"calculate-tee-times\": false,"
" \"t1-percent\": .40,"
- " \"t2-percent\": .80"
+ " \"t2-percent\": .80,"
+ " \"cache-threshold\": .15,"
+ " \"cache-max-age\": 5"
" }"
" ]"
"}";
EXPECT_EQ("[^A-Z]", network->getHostnameCharSet().get());
EXPECT_EQ("x", network->getHostnameCharReplacement().get());
EXPECT_TRUE(network->getStoreExtendedInfo().get());
+ EXPECT_EQ(0.123, network->getCacheThreshold());
+ EXPECT_EQ(123, network->getCacheMaxAge());
+ EXPECT_TRUE(network->getDdnsUpdateOnRenew().get());
// Relay information.
auto relay_info = network->getRelayInfo();
" \"hostname-char-set\": \"[^A-Z]\","
" \"hostname-char-replacement\": \"x\","
" \"store-extended-info\": true,"
+ " \"cache-threshold\": 0.123,"
+ " \"cache-max-age\": 123,"
+ " \"ddns-update-on-renew\": true,"
" \"option-data\": ["
" {"
" \"name\": \"dns-servers\","
EXPECT_EQ("[^A-Z]", network->getHostnameCharSet().get());
EXPECT_EQ("x", network->getHostnameCharReplacement().get());
EXPECT_TRUE(network->getStoreExtendedInfo().get());
+ EXPECT_EQ(0.123, network->getCacheThreshold());
+ EXPECT_EQ(123, network->getCacheMaxAge());
+ EXPECT_TRUE(network->getDdnsUpdateOnRenew().get());
// Relay information.
auto relay_info = network->getRelayInfo();
EXPECT_TRUE(network->getHostnameCharReplacement().unspecified());
EXPECT_TRUE(network->getHostnameCharReplacement().empty());
+
+ EXPECT_TRUE(network->getDdnsUpdateOnRenew().unspecified());
+ EXPECT_FALSE(network->getDdnsSendUpdates().get());
}
// This test verifies that shared network can be given a name and that
// Configure global host sanitizing.
conf.addConfiguredGlobal("hostname-char-set", Element::create("[^A-Z]"));
conf.addConfiguredGlobal("hostname-char-replacement", Element::create("x"));
+ // Enable conflict resolution globally.
+ conf.addConfiguredGlobal("ddns-use-conflict-resolution", Element::create(true));
// Add a plain subnet
Triplet<uint32_t> def_triplet;
subnet2->setDdnsGeneratedPrefix("prefix");
subnet2->setDdnsQualifyingSuffix("example.com.");
subnet2->setHostnameCharSet("");
+ subnet2->setDdnsUpdateOnRenew(true);
+ subnet2->setDdnsUseConflictResolution(false);
// Get DDNS params for subnet1.
ASSERT_NO_THROW(params = conf_.getDdnsParams(subnet1));
EXPECT_TRUE(params->getQualifyingSuffix().empty());
EXPECT_EQ("[^A-Z]", params->getHostnameCharSet());
EXPECT_EQ("x", params->getHostnameCharReplacement());
+ EXPECT_FALSE(params->getUpdateOnRenew());
+ EXPECT_TRUE(params->getUseConflictResolution());
// We inherited a non-blank hostname_char_set so we
// should get a sanitizer instance.
EXPECT_EQ("example.com.", params->getQualifyingSuffix());
EXPECT_EQ("", params->getHostnameCharSet());
EXPECT_EQ("x", params->getHostnameCharReplacement());
+ EXPECT_TRUE(params->getUpdateOnRenew());
+ EXPECT_FALSE(params->getUseConflictResolution());
// We have a blank hostname-char-set so we should not get a sanitizer instance.
ASSERT_NO_THROW(sanitizer = params->getHostnameSanitizer());
conf.addConfiguredGlobal("ddns-qualifying-suffix", Element::create("example.com"));
conf.addConfiguredGlobal("hostname-char-set", Element::create("[^A-Z]"));
conf.addConfiguredGlobal("hostname-char-replacement", Element::create("x"));
+ conf.addConfiguredGlobal("ddns-update-on-renew", Element::create(true));
+ conf.addConfiguredGlobal("ddns-use-conflict-resolution", Element::create(false));
// Get DDNS params for no subnet.
Subnet4Ptr subnet4;
EXPECT_TRUE(params->getQualifyingSuffix().empty());
EXPECT_TRUE(params->getHostnameCharSet().empty());
EXPECT_TRUE(params->getHostnameCharReplacement().empty());
+ EXPECT_FALSE(params->getUpdateOnRenew());
+ EXPECT_TRUE(params->getUseConflictResolution());
}
// Verifies that the scoped values for DDNS parameters can be fetched
// Configure global host sanitizing.
conf.addConfiguredGlobal("hostname-char-set", Element::create("[^A-Z]"));
conf.addConfiguredGlobal("hostname-char-replacement", Element::create("x"));
+ // Enable conflict resolution globally.
+ conf.addConfiguredGlobal("ddns-use-conflict-resolution", Element::create(true));
+
// Add a plain subnet
Triplet<uint32_t> def_triplet;
Subnet6Ptr subnet1(new Subnet6(IOAddress("2001:db8:1::"), 64,
subnet2->setDdnsGeneratedPrefix("prefix");
subnet2->setDdnsQualifyingSuffix("example.com.");
subnet2->setHostnameCharSet("");
+ subnet2->setDdnsUpdateOnRenew(true);
+ subnet2->setDdnsUseConflictResolution(false);
// Get DDNS params for subnet1.
ASSERT_NO_THROW(params = conf_.getDdnsParams(subnet1));
EXPECT_TRUE(params->getQualifyingSuffix().empty());
EXPECT_EQ("[^A-Z]", params->getHostnameCharSet());
EXPECT_EQ("x", params->getHostnameCharReplacement());
+ EXPECT_FALSE(params->getUpdateOnRenew());
+ EXPECT_TRUE(params->getUseConflictResolution());
// We inherited a non-blank hostname_char_set so we
// should get a sanitizer instance.
// Get DDNS params for subnet2.
ASSERT_NO_THROW(params = conf_.getDdnsParams(subnet2));
- // Verify subnet1 values are right. Note, updates should be disabled,
+ // Verify subnet2 values are right. Note, updates should be disabled,
// because D2Client is disabled.
EXPECT_FALSE(params->getEnableUpdates());
EXPECT_TRUE(params->getOverrideNoUpdate());
EXPECT_EQ("example.com.", params->getQualifyingSuffix());
EXPECT_EQ("", params->getHostnameCharSet());
EXPECT_EQ("x", params->getHostnameCharReplacement());
+ EXPECT_TRUE(params->getUpdateOnRenew());
+ EXPECT_FALSE(params->getUseConflictResolution());
// We have a blank hostname-char-set so we should not get a sanitizer instance.
ASSERT_NO_THROW(sanitizer = params->getHostnameSanitizer());
conf.addConfiguredGlobal("ddns-qualifying-suffix", Element::create("example.com"));
conf.addConfiguredGlobal("hostname-char-set", Element::create("[^A-Z]"));
conf.addConfiguredGlobal("hostname-char-replacement", Element::create("x"));
+ conf.addConfiguredGlobal("ddns-update-on-renew", Element::create(true));
+ conf.addConfiguredGlobal("ddns-use-conflict-resolution", Element::create(false));
// Get DDNS params for no subnet.
Subnet6Ptr subnet6;
EXPECT_TRUE(subnet.getHostnameCharReplacement().unspecified());
EXPECT_TRUE(subnet.getHostnameCharReplacement().empty());
+
+ EXPECT_TRUE(subnet.getDdnsUpdateOnRenew().unspecified());
+ EXPECT_FALSE(subnet.getDdnsUpdateOnRenew().get());
}
// Checks that the subnet id can be either autogenerated or set to an
EXPECT_TRUE(subnet.getHostnameCharReplacement().unspecified());
EXPECT_TRUE(subnet.getHostnameCharReplacement().empty());
+
+ EXPECT_TRUE(subnet.getDdnsUpdateOnRenew().unspecified());
+ EXPECT_FALSE(subnet.getDdnsUpdateOnRenew().get());
}
// Checks that the subnet id can be either autogenerated or set to an