Kea: d2_update_mgr.cc Source File

| Kea 2.2.0

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