| Kea 3.0.2-git

d2_update_mgr.cc
Go to the documentation of this file.
1// Copyright (C) 2013-2024 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>
15#include <d2/check_exists_remove.h>
16#include <d2/simple_add_without_dhcid.h>
17#include <d2/simple_remove_without_dhcid.h>
18
19#include <sstream>
20#include <iostream>
21#include <vector>
22
23 namespace isc {
24 namespace d2 {
25
26 const size_t D2UpdateMgr::MAX_TRANSACTIONS_DEFAULT;
27
28 D2UpdateMgr::D2UpdateMgr(D2QueueMgrPtr& queue_mgr, D2CfgMgrPtr& cfg_mgr,
29 asiolink::IOServicePtr& io_service,
30 const size_t max_transactions)
31 :queue_mgr_(queue_mgr), cfg_mgr_(cfg_mgr), io_service_(io_service) {
32 if (!queue_mgr_) {
33 isc_throw(D2UpdateMgrError, "D2UpdateMgr queue manager cannot be null");
34 }
35
36 if (!cfg_mgr_) {
37 isc_throw(D2UpdateMgrError,
38 "D2UpdateMgr configuration manager cannot be null");
39 }
40
41 if (!io_service_) {
42 isc_throw(D2UpdateMgrError, "IOServicePtr cannot be null");
43 }
44
45 // Use setter to do validation.
46 setMaxTransactions(max_transactions);
47}
48
49 D2UpdateMgr::~D2UpdateMgr() {
50 transaction_list_.clear();
51}
52
53 void D2UpdateMgr::sweep() {
54 // cleanup finished transactions;
55 checkFinishedTransactions();
56
57 // if the queue isn't empty, find the next suitable job and
58 // start a transaction for it.
59 // @todo - Do we want to queue max transactions? The logic here will only
60 // start one new transaction per invocation. On the other hand a busy
61 // system will generate many IO events and this method will be called
62 // frequently. It will likely achieve max transactions quickly on its own.
63 if (getQueueCount() > 0) {
64 if (getTransactionCount() >= max_transactions_) {
65 LOG_DEBUG(dhcp_to_d2_logger, isc::log::DBGLVL_TRACE_DETAIL_DATA,
66 DHCP_DDNS_AT_MAX_TRANSACTIONS).arg(getQueueCount())
67 .arg(getMaxTransactions());
68
69 return;
70 }
71
72 // We are not at maximum transactions, so pick and start the next job.
73 pickNextJob();
74 }
75}
76
77 void
78 D2UpdateMgr::checkFinishedTransactions() {
79 // Cycle through transaction list and do whatever needs to be done
80 // for finished transactions.
81 // At the moment all we do is remove them from the list. This is likely
82 // to expand as DHCP_DDNS matures.
83 // NOTE: One must use postfix increments of the iterator on the calls
84 // to erase. This replaces the old iterator which becomes invalid by the
85 // erase with the next valid iterator. Prefix incrementing will not
86 // work.
87 TransactionList::iterator it = transaction_list_.begin();
88 while (it != transaction_list_.end()) {
89 NameChangeTransactionPtr trans = (*it).second;
90 if (trans->isModelDone()) {
91 // @todo Additional actions based on NCR status could be
92 // performed here.
93 transaction_list_.erase(it++);
94 } else {
95 ++it;
96 }
97 }
98}
99
100 void D2UpdateMgr::pickNextJob() {
101 // Start at the front of the queue, looking for the first entry for
102 // which no transaction is in progress. If we find an eligible entry
103 // remove it from the queue and make a transaction for it. If the
104 // transaction creation fails try the next entry in the queue.
105 // Requests and transactions are associated by DHCID. If a request has
106 // the same DHCID as a transaction, they are presumed to be for the same
107 // "end user".
108 size_t queue_count = getQueueCount();
109 for (size_t index = 0; index < queue_count; ) {
110 dhcp_ddns::NameChangeRequestPtr found_ncr = queue_mgr_->peekAt(index);
111 if (hasTransaction(found_ncr->getDhcid())) {
112 // Leave it on the queue, move on to the next.
113 ++index;
114 } else {
115 // Dequeue it and try to make transaction for it.
116 queue_mgr_->dequeueAt(index);
117 if (makeTransaction(found_ncr)) {
118 return;
119 }
120
121 // One less in the queue.
122 --queue_count;
123 }
124 }
125
126 // There were no eligible jobs. All of the current DHCIDs already have
127 // transactions pending.
128 LOG_DEBUG(dhcp_to_d2_logger, isc::log::DBGLVL_TRACE_DETAIL_DATA,
129 DHCP_DDNS_NO_ELIGIBLE_JOBS)
130 .arg(getQueueCount()).arg(getTransactionCount());
131}
132
133 bool
134 D2UpdateMgr::makeTransaction(dhcp_ddns::NameChangeRequestPtr& next_ncr) {
135 // First lets ensure there is not a transaction in progress for this
136 // DHCID. (pickNextJob should ensure this, as it is the only real caller
137 // but for safety's sake we'll check).
138 const TransactionKey& key = next_ncr->getDhcid();
139 if (findTransaction(key) != transactionListEnd()) {
140 // This is programmatic error. Caller(s) should be checking this.
141 isc_throw(D2UpdateMgrError, "Transaction already in progress for: "
142 << key.toStr());
143 }
144
145 int direction_count = 0;
146 // If forward change is enabled, match to forward servers.
147 DdnsDomainPtr forward_domain;
148 if (next_ncr->isForwardChange()) {
149 if (!cfg_mgr_->forwardUpdatesEnabled()) {
150 next_ncr->setForwardChange(false);
151 LOG_DEBUG(dhcp_to_d2_logger, isc::log::DBGLVL_TRACE_DETAIL_DATA,
152 DHCP_DDNS_FWD_REQUEST_IGNORED)
153 .arg(next_ncr->getRequestId())
154 .arg(next_ncr->toText());
155 } else {
156 bool matched = cfg_mgr_->matchForward(next_ncr->getFqdn(),
157 forward_domain);
158 // Could not find a match for forward DNS server. Log it and get
159 // out. This has the net affect of dropping the request on the
160 // floor.
161 if (!matched) {
162 LOG_ERROR(dhcp_to_d2_logger, DHCP_DDNS_NO_FWD_MATCH_ERROR)
163 .arg(next_ncr->getRequestId())
164 .arg(next_ncr->toText());
165 return (false);
166 }
167
168 ++direction_count;
169 }
170 }
171
172 // If reverse change is enabled, match to reverse servers.
173 DdnsDomainPtr reverse_domain;
174 if (next_ncr->isReverseChange()) {
175 if (!cfg_mgr_->reverseUpdatesEnabled()) {
176 next_ncr->setReverseChange(false);
177 LOG_DEBUG(dhcp_to_d2_logger, isc::log::DBGLVL_TRACE_DETAIL_DATA,
178 DHCP_DDNS_REV_REQUEST_IGNORED)
179 .arg(next_ncr->getRequestId())
180 .arg(next_ncr->toText());
181 } else {
182 bool matched = cfg_mgr_->matchReverse(next_ncr->getIpAddress(),
183 reverse_domain);
184 // Could not find a match for reverse DNS server. Log it and get
185 // out. This has the net affect of dropping the request on the
186 // floor.
187 if (!matched) {
188 LOG_ERROR(dhcp_to_d2_logger, DHCP_DDNS_NO_REV_MATCH_ERROR)
189 .arg(next_ncr->getRequestId())
190 .arg(next_ncr->toText());
191 return (false);
192 }
193
194 ++direction_count;
195 }
196 }
197
198 // If there is nothing to actually do, then the request falls on the floor.
199 // Should we log this?
200 if (!direction_count) {
201 LOG_DEBUG(dhcp_to_d2_logger, isc::log::DBGLVL_TRACE_DETAIL_DATA,
202 DHCP_DDNS_REQUEST_DROPPED)
203 .arg(next_ncr->getRequestId())
204 .arg(next_ncr->toText());
205 return (false);
206 }
207
208 // We matched to the required servers, so construct the transaction.
209 // @todo If multi-threading is implemented, one would pass in an
210 // empty IOServicePtr, rather than our instance value. This would cause
211 // the transaction to instantiate its own, separate IOService to handle
212 // the transaction's IO.
213 NameChangeTransactionPtr trans;
214 if (next_ncr->getChangeType() == dhcp_ddns::CHG_ADD) {
215 switch(next_ncr->getConflictResolutionMode()) {
216 case dhcp_ddns::CHECK_WITH_DHCID:
217 trans.reset(new NameAddTransaction(io_service_, next_ncr,
218 forward_domain, reverse_domain,
219 cfg_mgr_));
220 break;
221 case dhcp_ddns::CHECK_EXISTS_WITH_DHCID:
222 trans.reset(new CheckExistsAddTransaction(io_service_, next_ncr,
223 forward_domain, reverse_domain,
224 cfg_mgr_));
225 break;
226 case dhcp_ddns::NO_CHECK_WITHOUT_DHCID:
227 trans.reset(new SimpleAddWithoutDHCIDTransaction(io_service_, next_ncr,
228 forward_domain, reverse_domain,
229 cfg_mgr_));
230 break;
231 default:
232 // dhcp_ddns::NO_CHECK_WITH_DHCID
233 trans.reset(new SimpleAddTransaction(io_service_, next_ncr,
234 forward_domain, reverse_domain,
235 cfg_mgr_));
236 break;
237 }
238 } else {
239 switch(next_ncr->getConflictResolutionMode()) {
240 case dhcp_ddns::CHECK_WITH_DHCID:
241 trans.reset(new NameRemoveTransaction(io_service_, next_ncr,
242 forward_domain, reverse_domain,
243 cfg_mgr_));
244 break;
245 case dhcp_ddns::CHECK_EXISTS_WITH_DHCID:
246 trans.reset(new CheckExistsRemoveTransaction(io_service_, next_ncr,
247 forward_domain, reverse_domain,
248 cfg_mgr_));
249 break;
250 case dhcp_ddns::NO_CHECK_WITHOUT_DHCID:
251 trans.reset(new SimpleRemoveWithoutDHCIDTransaction(io_service_, next_ncr,
252 forward_domain, reverse_domain,
253 cfg_mgr_));
254 break;
255 default:
256 // dhcp_ddns::NO_CHECK_WITH_DHCID
257 trans.reset(new SimpleRemoveTransaction(io_service_, next_ncr,
258 forward_domain, reverse_domain,
259 cfg_mgr_));
260 break;
261 }
262 }
263
264 // Add the new transaction to the list.
265 transaction_list_[key] = trans;
266
267 // Start it.
268 trans->startTransaction();
269 return (true);
270}
271
272TransactionList::iterator
273 D2UpdateMgr::findTransaction(const TransactionKey& key) {
274 return (transaction_list_.find(key));
275}
276
277 bool
278 D2UpdateMgr::hasTransaction(const TransactionKey& key) {
279 return (findTransaction(key) != transactionListEnd());
280}
281
282 void
283 D2UpdateMgr::removeTransaction(const TransactionKey& key) {
284 TransactionList::iterator pos = findTransaction(key);
285 if (pos != transactionListEnd()) {
286 transaction_list_.erase(pos);
287 }
288}
289
290TransactionList::iterator
291 D2UpdateMgr::transactionListBegin() {
292 return (transaction_list_.begin());
293}
294
295TransactionList::iterator
296 D2UpdateMgr::transactionListEnd() {
297 return (transaction_list_.end());
298}
299
300 void
301 D2UpdateMgr::clearTransactionList() {
302 // @todo for now this just wipes them out. We might need something
303 // more elegant, that allows a cancel first.
304 transaction_list_.clear();
305}
306
307 void
308 D2UpdateMgr::setMaxTransactions(const size_t new_trans_max) {
309 // Obviously we need at room for at least one transaction.
310 if (new_trans_max < 1) {
311 isc_throw(D2UpdateMgrError, "D2UpdateMgr"
312 " maximum transactions limit must be greater than zero");
313 }
314
315 // Do not allow the list maximum to be set to less then current list size.
316 if (new_trans_max < getTransactionCount()) {
317 isc_throw(D2UpdateMgrError, "D2UpdateMgr maximum transaction limit "
318 "cannot be less than the current transaction count :"
319 << getTransactionCount());
320 }
321
322 max_transactions_ = new_trans_max;
323}
324
325 size_t
326 D2UpdateMgr::getQueueCount() const {
327 return (queue_mgr_->getQueueSize());
328}
329
330 size_t
331 D2UpdateMgr::getTransactionCount() const {
332 return (transaction_list_.size());
333}
334
335
336} // namespace isc::d2
337} // namespace isc
check_exists_add.h
This file defines the class CheckExistsAddTransaction.
check_exists_remove.h
This file defines the class CheckExistsRemoveTransaction.
isc::d2::CheckExistsAddTransaction
Embodies the "life-cycle" required to carry out a DDNS Add update.
Definition check_exists_add.h:52
isc::d2::CheckExistsRemoveTransaction
Embodies the "life-cycle" required to carry out a DDNS Remove update.
Definition check_exists_remove.h:51
isc::d2::D2UpdateMgrError
Thrown if the update manager encounters a general error.
Definition d2_update_mgr.h:27
isc::d2::D2UpdateMgr::transactionListEnd
TransactionList::iterator transactionListEnd()
Returns the transaction list end position.
Definition d2_update_mgr.cc:296
isc::d2::D2UpdateMgr::hasTransaction
bool hasTransaction(const TransactionKey &key)
Convenience method that checks transaction list for the given key.
Definition d2_update_mgr.cc:278
isc::d2::D2UpdateMgr::findTransaction
TransactionList::iterator findTransaction(const TransactionKey &key)
Search the transaction list for the given key.
Definition d2_update_mgr.cc:273
isc::d2::D2UpdateMgr::~D2UpdateMgr
virtual ~D2UpdateMgr()
Destructor.
Definition d2_update_mgr.cc:49
isc::d2::D2UpdateMgr::removeTransaction
void removeTransaction(const TransactionKey &key)
Removes the entry pointed to by key from the transaction list.
Definition d2_update_mgr.cc:283
isc::d2::D2UpdateMgr::makeTransaction
bool makeTransaction(isc::dhcp_ddns::NameChangeRequestPtr &ncr)
Create a new transaction for the given request.
Definition d2_update_mgr.cc:134
isc::d2::D2UpdateMgr::checkFinishedTransactions
void checkFinishedTransactions()
Performs post-completion cleanup on completed transactions.
Definition d2_update_mgr.cc:78
isc::d2::D2UpdateMgr::setMaxTransactions
void setMaxTransactions(const size_t max_transactions)
Sets the maximum number of entries allowed in the queue.
Definition d2_update_mgr.cc:308
isc::d2::D2UpdateMgr::transactionListBegin
TransactionList::iterator transactionListBegin()
Returns the transaction list beg position.
Definition d2_update_mgr.cc:291
isc::d2::D2UpdateMgr::clearTransactionList
void clearTransactionList()
Immediately discards all entries in the transaction list.
Definition d2_update_mgr.cc:301
isc::d2::D2UpdateMgr::sweep
void sweep()
Check current transactions; start transactions for new requests.
Definition d2_update_mgr.cc:53
isc::d2::D2UpdateMgr::getMaxTransactions
size_t getMaxTransactions() const
Returns the maximum number of concurrent transactions.
Definition d2_update_mgr.h:167
isc::d2::D2UpdateMgr::getQueueCount
size_t getQueueCount() const
Convenience method that returns the number of requests queued.
Definition d2_update_mgr.cc:326
isc::d2::D2UpdateMgr::D2UpdateMgr
D2UpdateMgr(D2QueueMgrPtr &queue_mgr, D2CfgMgrPtr &cfg_mgr, asiolink::IOServicePtr &io_service, const size_t max_transactions=MAX_TRANSACTIONS_DEFAULT)
Constructor.
Definition d2_update_mgr.cc:28
isc::d2::D2UpdateMgr::getTransactionCount
size_t getTransactionCount() const
Returns the current number of transactions.
Definition d2_update_mgr.cc:331
isc::d2::D2UpdateMgr::pickNextJob
void pickNextJob()
Starts a transaction for the next eligible request in the queue.
Definition d2_update_mgr.cc:100
isc::d2::D2UpdateMgr::MAX_TRANSACTIONS_DEFAULT
static const size_t MAX_TRANSACTIONS_DEFAULT
Maximum number of concurrent transactions NOTE that 32 is an arbitrary choice picked for the initial ...
Definition d2_update_mgr.h:70
isc::d2::NameAddTransaction
Embodies the "life-cycle" required to carry out a DDNS Add update.
Definition nc_add.h:52
isc::d2::NameRemoveTransaction
Embodies the "life-cycle" required to carry out a DDNS Remove update.
Definition nc_remove.h:51
isc::d2::SimpleAddTransaction
Embodies the "life-cycle" required to carry out a DDNS Add update.
Definition simple_add.h:47
isc::d2::SimpleAddWithoutDHCIDTransaction
Embodies the "life-cycle" required to carry out a DDNS Add update without checking for conflicts.
Definition simple_add_without_dhcid.h:49
isc::d2::SimpleRemoveTransaction
Embodies the "life-cycle" required to carry out a DDNS Remove update.
Definition simple_remove.h:49
isc::d2::SimpleRemoveWithoutDHCIDTransaction
Embodies the "life-cycle" required to carry out a DDNS Remove update without removing any matching DH...
Definition simple_remove_without_dhcid.h:49
isc::dhcp_ddns::D2Dhcid::toStr
std::string toStr() const
Returns the DHCID value as a string of hexadecimal digits.
Definition ncr_msg.cc:135
d2_update_mgr.h
This file defines the class D2UpdateMgr.
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition exceptions/exceptions.h:210
LOG_ERROR
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition macros.h:32
LOG_DEBUG
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition macros.h:14
isc::asiolink::IOServicePtr
boost::shared_ptr< IOService > IOServicePtr
Defines a smart pointer to an IOService instance.
Definition io_service.h:28
isc::d2
Definition check_exists_add.cc:19
isc::d2::DHCP_DDNS_REQUEST_DROPPED
const isc::log::MessageID DHCP_DDNS_REQUEST_DROPPED
Definition d2_messages.h:69
isc::d2::DdnsDomainPtr
boost::shared_ptr< DdnsDomain > DdnsDomainPtr
Defines a pointer for DdnsDomain instances.
Definition d2_config.h:624
isc::d2::D2CfgMgrPtr
boost::shared_ptr< D2CfgMgr > D2CfgMgrPtr
Defines a shared pointer to D2CfgMgr.
Definition d2_cfg_mgr.h:367
isc::d2::DHCP_DDNS_NO_REV_MATCH_ERROR
const isc::log::MessageID DHCP_DDNS_NO_REV_MATCH_ERROR
Definition d2_messages.h:52
isc::d2::DHCP_DDNS_FWD_REQUEST_IGNORED
const isc::log::MessageID DHCP_DDNS_FWD_REQUEST_IGNORED
Definition d2_messages.h:45
isc::d2::DHCP_DDNS_REV_REQUEST_IGNORED
const isc::log::MessageID DHCP_DDNS_REV_REQUEST_IGNORED
Definition d2_messages.h:82
isc::d2::dhcp_to_d2_logger
isc::log::Logger dhcp_to_d2_logger("dhcp-to-d2")
Definition d2_log.h:19
isc::d2::DHCP_DDNS_AT_MAX_TRANSACTIONS
const isc::log::MessageID DHCP_DDNS_AT_MAX_TRANSACTIONS
Definition d2_messages.h:13
isc::d2::NameChangeTransactionPtr
boost::shared_ptr< NameChangeTransaction > NameChangeTransactionPtr
Defines a pointer to a NameChangeTransaction.
Definition nc_trans.h:598
isc::d2::D2QueueMgrPtr
boost::shared_ptr< D2QueueMgr > D2QueueMgrPtr
Defines a pointer for manager instances.
Definition d2_queue_mgr.h:340
isc::d2::DHCP_DDNS_NO_ELIGIBLE_JOBS
const isc::log::MessageID DHCP_DDNS_NO_ELIGIBLE_JOBS
Definition d2_messages.h:49
isc::d2::TransactionKey
isc::dhcp_ddns::D2Dhcid TransactionKey
Defines the type used as the unique key for transactions.
Definition nc_trans.h:35
isc::d2::DHCP_DDNS_NO_FWD_MATCH_ERROR
const isc::log::MessageID DHCP_DDNS_NO_FWD_MATCH_ERROR
Definition d2_messages.h:50
isc::dhcp_ddns::CHECK_WITH_DHCID
@ CHECK_WITH_DHCID
Definition ncr_msg.h:65
isc::dhcp_ddns::NO_CHECK_WITHOUT_DHCID
@ NO_CHECK_WITHOUT_DHCID
Definition ncr_msg.h:68
isc::dhcp_ddns::CHECK_EXISTS_WITH_DHCID
@ CHECK_EXISTS_WITH_DHCID
Definition ncr_msg.h:67
isc::dhcp_ddns::NameChangeRequestPtr
boost::shared_ptr< NameChangeRequest > NameChangeRequestPtr
Defines a pointer to a NameChangeRequest.
Definition ncr_msg.h:241
isc::dhcp_ddns::CHG_ADD
@ CHG_ADD
Definition ncr_msg.h:46
isc::log::DBGLVL_TRACE_DETAIL_DATA
const int DBGLVL_TRACE_DETAIL_DATA
Trace data associated with detailed operations.
Definition log_dbglevels.h:78
isc
Defines the logger used by the top-level component of kea-lfc.
Definition agent_parser.cc:148
nc_add.h
This file defines the class NameAddTransaction.
nc_remove.h
This file defines the class NameRemoveTransaction.
simple_add.h
simple_add_without_dhcid.h
This file defines the class SimpleAddWithoutDHCIDTransaction.
simple_remove.h
simple_remove_without_dhcid.h
This file defines the class SimpleRemoveWithoutDHCIDTransaction.