Kea 3.2.1-git
d2_update_mgr.cc
Go to the documentation of this file.
1// Copyright (C) 2013-2026 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
9#include <d2/d2_update_mgr.h>
10#include <d2/nc_add.h>
11#include <d2/nc_remove.h>
12#include <d2/simple_add.h>
13#include <d2/simple_remove.h>
14#include <d2/check_exists_add.h>
18
19#include <sstream>
20#include <iostream>
21#include <vector>
22
23using namespace isc::dhcp_ddns;
24
25namespace isc {
26namespace d2 {
27
29
31 asiolink::IOServicePtr& io_service,
32 const size_t max_transactions)
33 :queue_mgr_(queue_mgr), cfg_mgr_(cfg_mgr), io_service_(io_service) {
34 if (!queue_mgr_) {
35 isc_throw(D2UpdateMgrError, "D2UpdateMgr queue manager cannot be null");
36 }
37
38 if (!cfg_mgr_) {
40 "D2UpdateMgr configuration manager cannot be null");
41 }
42
43 if (!io_service_) {
44 isc_throw(D2UpdateMgrError, "IOServicePtr cannot be null");
45 }
46
47 // Use setter to do validation.
48 setMaxTransactions(max_transactions);
49}
50
52 transaction_store_.clear();
53}
54
56 // cleanup finished transactions;
58
59 // if the queue isn't empty, find the next suitable job and
60 // start a transaction for it.
61 // @todo - Do we want to queue max transactions? The logic here will only
62 // start one new transaction per invocation. On the other hand a busy
63 // system will generate many IO events and this method will be called
64 // frequently. It will likely achieve max transactions quickly on its own.
65 if (getQueueCount() > 0) {
66 if (getTransactionCount() >= max_transactions_) {
69 .arg(getMaxTransactions());
70
71 return;
72 }
73
74 // We are not at maximum transactions, so pick and start the next job.
76 }
77}
78
79void
81 // Cycle through transaction list and do whatever needs to be done
82 // for finished transactions.
83 // At the moment all we do is remove them from the list. This is likely
84 // to expand as DHCP_DDNS matures.
85 auto& sidx = transaction_store_.get<SequenceTag>();
86 auto siter = sidx.begin();
87 while (siter != sidx.end()) {
88 if ((*siter)->isModelDone()) {
89 siter = transaction_store_.get<SequenceTag>().erase(siter);
90 } else {
91 ++siter;
92 }
93 }
94}
95
97 // Start at the front of the queue, looking for the first request for
98 // which no transaction is working the same FQDN or ip address. If a
99 // request is currently ineligible we levae it on the queue, and try
100 // the next one. If it is eligible wee remove it from the queue and
101 // make a transaction for it. If the transaction creation fails try
102 // the next request in the queue.
103 size_t queue_count = getQueueCount();
104 for (size_t index = 0; index < queue_count; ) {
105 NameChangeRequestPtr queued_ncr = queue_mgr_->peekAt(index);
106 if (hasTransaction(queued_ncr)) {
107 // Leave it on the queue, move on to the next.
108 ++index;
109 } else {
110 // Dequeue it and try to make transaction for it.
111 queue_mgr_->dequeueAt(index);
112 if (makeTransaction(queued_ncr)) {
113 return;
114 }
115
116 // One less in the queue.
117 --queue_count;
118 }
119 }
120
121 // There were no eligible jobs. All of the current DHCIDs already have
122 // transactions pending.
125 .arg(getQueueCount()).arg(getTransactionCount());
126}
127
128bool
130 int direction_count = 0;
131 // If forward change is enabled, match to forward servers.
132 DdnsDomainPtr forward_domain;
133 if (next_ncr->isForwardChange()) {
134 if (!cfg_mgr_->forwardUpdatesEnabled()) {
135 next_ncr->setForwardChange(false);
138 .arg(next_ncr->getRequestId())
139 .arg(next_ncr->toText());
140 } else {
141 bool matched = cfg_mgr_->matchForward(next_ncr->getFqdn(),
142 forward_domain);
143 // Could not find a match for forward DNS server. Log it and get
144 // out. This has the net affect of dropping the request on the
145 // floor.
146 if (!matched) {
148 .arg(next_ncr->getRequestId())
149 .arg(next_ncr->toText());
150 return (false);
151 }
152
153 ++direction_count;
154 }
155 }
156
157 // If reverse change is enabled, match to reverse servers.
158 DdnsDomainPtr reverse_domain;
159 if (next_ncr->isReverseChange()) {
160 if (!cfg_mgr_->reverseUpdatesEnabled()) {
161 next_ncr->setReverseChange(false);
164 .arg(next_ncr->getRequestId())
165 .arg(next_ncr->toText());
166 } else {
167 bool matched = cfg_mgr_->matchReverse(next_ncr->getIpAddress(),
168 reverse_domain);
169 // Could not find a match for reverse DNS server. Log it and get
170 // out. This has the net affect of dropping the request on the
171 // floor.
172 if (!matched) {
174 .arg(next_ncr->getRequestId())
175 .arg(next_ncr->toText());
176 return (false);
177 }
178
179 ++direction_count;
180 }
181 }
182
183 // If there is nothing to actually do, then the request falls on the floor.
184 // Should we log this?
185 if (!direction_count) {
188 .arg(next_ncr->getRequestId())
189 .arg(next_ncr->toText());
190 return (false);
191 }
192
193 // We matched to the required servers, so construct the transaction.
194 // @todo If multi-threading is implemented, one would pass in an
195 // empty IOServicePtr, rather than our instance value. This would cause
196 // the transaction to instantiate its own, separate IOService to handle
197 // the transaction's IO.
199 if (next_ncr->getChangeType() == CHG_ADD) {
200 switch(next_ncr->getConflictResolutionMode()) {
201 case CHECK_WITH_DHCID:
202 trans.reset(new NameAddTransaction(io_service_, next_ncr,
203 forward_domain, reverse_domain,
204 cfg_mgr_));
205 break;
207 trans.reset(new CheckExistsAddTransaction(io_service_, next_ncr,
208 forward_domain, reverse_domain,
209 cfg_mgr_));
210 break;
212 trans.reset(new SimpleAddWithoutDHCIDTransaction(io_service_, next_ncr,
213 forward_domain, reverse_domain,
214 cfg_mgr_));
215 break;
216 default:
217 // NO_CHECK_WITH_DHCID
218 trans.reset(new SimpleAddTransaction(io_service_, next_ncr,
219 forward_domain, reverse_domain,
220 cfg_mgr_));
221 break;
222 }
223 } else {
224 switch(next_ncr->getConflictResolutionMode()) {
225 case CHECK_WITH_DHCID:
226 trans.reset(new NameRemoveTransaction(io_service_, next_ncr,
227 forward_domain, reverse_domain,
228 cfg_mgr_));
229 break;
231 trans.reset(new CheckExistsRemoveTransaction(io_service_, next_ncr,
232 forward_domain, reverse_domain,
233 cfg_mgr_));
234 break;
236 trans.reset(new SimpleRemoveWithoutDHCIDTransaction(io_service_, next_ncr,
237 forward_domain, reverse_domain,
238 cfg_mgr_));
239 break;
240 default:
241 // NO_CHECK_WITH_DHCID
242 trans.reset(new SimpleRemoveTransaction(io_service_, next_ncr,
243 forward_domain, reverse_domain,
244 cfg_mgr_));
245 break;
246 }
247 }
248
249 // Add the new transaction to the store
250 auto ret = transaction_store_.push_back(trans);
251 if (ret.second == false) {
252 // Shouldn't happen, as pickNextJob() just checked.
253 isc_throw(D2UpdateMgrError, "Transaction already in progress for FQDN "
254 << trans->getFqdn() << " or address " << trans->getIOAddress());
255 }
256
257 // Start it.
258 trans->startTransaction();
259 return (true);
260}
261
262bool
264 if (!ncr) {
266 "D2UpdateMgr::hasTransaction() ncr cannot be null");
267 }
268
269 // Check for fqdn first.
270 auto& fidx = transaction_store_.get<FqdnTag>();
271 auto fiter = fidx.find(ncr->getFqdnLower());
272 if (fiter != fidx.end()) {
273 return (true);
274 }
275
276 // Check for fqdn first.
277 auto& aidx = transaction_store_.get<AddressTag>();
278 auto aiter = aidx.find(ncr->getIOAddress());
279 if (aiter != aidx.end()) {
280 return (true);
281 }
282
283 return (false);
284}
285
286void
288 // @todo for now this just wipes them out. We might need something
289 // more elegant, that allows a cancel first.
290 transaction_store_.clear();
291}
292
293void
294D2UpdateMgr::setMaxTransactions(const size_t new_trans_max) {
295 // Obviously we need at room for at least one transaction.
296 if (new_trans_max < 1) {
297 isc_throw(D2UpdateMgrError, "D2UpdateMgr"
298 " maximum transactions limit must be greater than zero");
299 }
300
301 // Do not allow the list maximum to be set to less then current list size.
302 if (new_trans_max < getTransactionCount()) {
303 isc_throw(D2UpdateMgrError, "D2UpdateMgr maximum transaction limit "
304 "cannot be less than the current transaction count :"
306 }
307
308 max_transactions_ = new_trans_max;
309}
310
311size_t
313 return (queue_mgr_->getQueueSize());
314}
315
316size_t
318 return (transaction_store_.size());
319}
320
321TransactionSequenceIndex::iterator
323 return (transaction_store_.get<SequenceTag>().begin());
324}
325
326TransactionSequenceIndex::iterator
328 return (transaction_store_.get<SequenceTag>().end());
329}
330
331TransactionFqdnIndex::iterator
332D2UpdateMgr::findTransactionByFqdn(const std::string& fqdn) const {
333 return (transaction_store_.get<FqdnTag>().find(fqdn));
334}
335
336TransactionFqdnIndex::iterator
338 return (transaction_store_.get<FqdnTag>().begin());
339}
340
341TransactionFqdnIndex::iterator
343 return (transaction_store_.get<FqdnTag>().end());
344}
345
346TransactionAddressIndex::iterator
348 return (transaction_store_.get<AddressTag>().find(address));
349}
350
351TransactionAddressIndex::iterator
353 return (transaction_store_.get<AddressTag>().begin());
354}
355
356TransactionAddressIndex::iterator
358 return (transaction_store_.get<AddressTag>().end());
359}
360
361
362} // namespace isc::d2
363} // namespace isc
This file defines the class CheckExistsAddTransaction.
This file defines the class CheckExistsRemoveTransaction.
CheckExistsAddTransaction(asiolink::IOServicePtr &io_service, dhcp_ddns::NameChangeRequestPtr &ncr, DdnsDomainPtr &forward_domain, DdnsDomainPtr &reverse_domain, D2CfgMgrPtr &cfg_mgr)
Constructor.
CheckExistsRemoveTransaction(asiolink::IOServicePtr &io_service, dhcp_ddns::NameChangeRequestPtr &ncr, DdnsDomainPtr &forward_domain, DdnsDomainPtr &reverse_domain, D2CfgMgrPtr &cfg_mgr)
Event sent when replace attempt to fails with address not in use.
Thrown if the update manager encounters a general error.
TransactionFqdnIndex::iterator transactionFqdnBegin()
Returns the transaction store begin iterator, ordered by FQDN.
TransactionAddressIndex::iterator transactionAddressEnd()
Returns the transaction store end iterator, ordered by address.
virtual ~D2UpdateMgr()
Destructor.
bool makeTransaction(isc::dhcp_ddns::NameChangeRequestPtr &ncr)
Create a new transaction for the given request.
TransactionFqdnIndex::iterator transactionFqdnEnd()
Returns the transaction store end iterator, ordered by FQDN.
TransactionAddressIndex::iterator transactionAddressBegin()
Returns the transaction store begin iterator, ordered by address.
TransactionSequenceIndex::iterator transactionSequenceEnd()
Returns the transaction store sequence end position.
void checkFinishedTransactions()
Performs post-completion cleanup on completed transactions.
void setMaxTransactions(const size_t max_transactions)
Sets the maximum number of entries allowed in the queue.
void sweep()
Check current transactions; start transactions for new requests.
TransactionAddressIndex::iterator findTransactionByAddress(const asiolink::IOAddress &address) const
Search the transaction store for a given address.
size_t getMaxTransactions() const
Returns the maximum number of concurrent transactions.
size_t getQueueCount() const
Convenience method that returns the number of requests queued.
bool hasTransaction(const dhcp_ddns::NameChangeRequestPtr &ncr) const
Checks if there is an active transaction matching the FQDN or ip address of an NCR.
D2UpdateMgr(D2QueueMgrPtr &queue_mgr, D2CfgMgrPtr &cfg_mgr, asiolink::IOServicePtr &io_service, const size_t max_transactions=MAX_TRANSACTIONS_DEFAULT)
Constructor.
size_t getTransactionCount() const
Returns the current number of transactions.
void pickNextJob()
Starts a transaction for the next eligible request in the queue.
void clearTransactions()
Immediately discards all entries in the transaction list.
TransactionFqdnIndex::iterator findTransactionByFqdn(const std::string &fqdn) const
Search the transaction store for an FQDN.
static const size_t MAX_TRANSACTIONS_DEFAULT
Maximum number of concurrent transactions NOTE that 32 is an arbitrary choice picked for the initial ...
TransactionSequenceIndex::iterator transactionSequenceBegin()
Returns the transaction store sequence begin position.
NameAddTransaction(asiolink::IOServicePtr &io_service, dhcp_ddns::NameChangeRequestPtr &ncr, DdnsDomainPtr &forward_domain, DdnsDomainPtr &reverse_domain, D2CfgMgrPtr &cfg_mgr)
Constructor.
Definition nc_add.cc:31
NameRemoveTransaction(asiolink::IOServicePtr &io_service, dhcp_ddns::NameChangeRequestPtr &ncr, DdnsDomainPtr &forward_domain, DdnsDomainPtr &reverse_domain, D2CfgMgrPtr &cfg_mgr)
Event sent when replace attempt to fails with address not in use.
Definition nc_remove.cc:28
SimpleAddTransaction(asiolink::IOServicePtr &io_service, dhcp_ddns::NameChangeRequestPtr &ncr, DdnsDomainPtr &forward_domain, DdnsDomainPtr &reverse_domain, D2CfgMgrPtr &cfg_mgr)
Constructor.
Definition simple_add.cc:30
SimpleAddWithoutDHCIDTransaction(asiolink::IOServicePtr &io_service, dhcp_ddns::NameChangeRequestPtr &ncr, DdnsDomainPtr &forward_domain, DdnsDomainPtr &reverse_domain, D2CfgMgrPtr &cfg_mgr)
Constructor.
SimpleRemoveTransaction(asiolink::IOServicePtr &io_service, dhcp_ddns::NameChangeRequestPtr &ncr, DdnsDomainPtr &forward_domain, DdnsDomainPtr &reverse_domain, D2CfgMgrPtr &cfg_mgr)
Event sent when replace attempt to fails with address not in use.
SimpleRemoveWithoutDHCIDTransaction(asiolink::IOServicePtr &io_service, dhcp_ddns::NameChangeRequestPtr &ncr, DdnsDomainPtr &forward_domain, DdnsDomainPtr &reverse_domain, D2CfgMgrPtr &cfg_mgr)
Event sent when replace attempt to fails with address not in use.
This file defines the class D2UpdateMgr.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition macros.h:32
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition macros.h:14
const isc::log::MessageID DHCP_DDNS_REQUEST_DROPPED
Definition d2_messages.h:69
boost::shared_ptr< DdnsDomain > DdnsDomainPtr
Defines a pointer for DdnsDomain instances.
Definition d2_config.h:624
boost::shared_ptr< D2CfgMgr > D2CfgMgrPtr
Defines a shared pointer to D2CfgMgr.
Definition d2_cfg_mgr.h:367
const isc::log::MessageID DHCP_DDNS_NO_REV_MATCH_ERROR
Definition d2_messages.h:52
const isc::log::MessageID DHCP_DDNS_FWD_REQUEST_IGNORED
Definition d2_messages.h:45
const isc::log::MessageID DHCP_DDNS_REV_REQUEST_IGNORED
Definition d2_messages.h:82
isc::log::Logger dhcp_to_d2_logger("dhcp-to-d2")
Definition d2_log.h:19
const isc::log::MessageID DHCP_DDNS_AT_MAX_TRANSACTIONS
Definition d2_messages.h:13
boost::shared_ptr< NameChangeTransaction > NameChangeTransactionPtr
Defines a pointer to a NameChangeTransaction.
Definition nc_trans.h:615
boost::shared_ptr< D2QueueMgr > D2QueueMgrPtr
Defines a pointer for manager instances.
const isc::log::MessageID DHCP_DDNS_NO_ELIGIBLE_JOBS
Definition d2_messages.h:49
const isc::log::MessageID DHCP_DDNS_NO_FWD_MATCH_ERROR
Definition d2_messages.h:50
@ NO_CHECK_WITHOUT_DHCID
Definition ncr_msg.h:69
@ CHECK_EXISTS_WITH_DHCID
Definition ncr_msg.h:68
boost::shared_ptr< NameChangeRequest > NameChangeRequestPtr
Defines a pointer to a NameChangeRequest.
Definition ncr_msg.h:242
const int DBGLVL_TRACE_DETAIL_DATA
Trace data associated with detailed operations.
Defines the logger used by the top-level component of kea-lfc.
This file defines the class NameAddTransaction.
This file defines the class NameRemoveTransaction.
This file defines the class SimpleAddWithoutDHCIDTransaction.
This file defines the class SimpleRemoveWithoutDHCIDTransaction.
Tag for index by ip-address.
Tag for index by Fqdn.
Tag for insertion order index.