Kea 1.5.0
d2_update_mgr.cc
Go to the documentation of this file.
1// Copyright (C) 2013-2017 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
13#include <sstream>
14#include <iostream>
15#include <vector>
16
17namespace isc {
18namespace d2 {
19
21
23 asiolink::IOServicePtr& io_service,
24 const size_t max_transactions)
25 :queue_mgr_(queue_mgr), cfg_mgr_(cfg_mgr), io_service_(io_service) {
26 if (!queue_mgr_) {
27 isc_throw(D2UpdateMgrError, "D2UpdateMgr queue manager cannot be null");
28 }
29
30 if (!cfg_mgr_) {
32 "D2UpdateMgr configuration manager cannot be null");
33 }
34
35 if (!io_service_) {
36 isc_throw(D2UpdateMgrError, "IOServicePtr cannot be null");
37 }
38
39 // Use setter to do validation.
40 setMaxTransactions(max_transactions);
41}
42
44 transaction_list_.clear();
45}
46
48 // cleanup finished transactions;
50
51 // if the queue isn't empty, find the next suitable job and
52 // start a transaction for it.
53 // @todo - Do we want to queue max transactions? The logic here will only
54 // start one new transaction per invocation. On the other hand a busy
55 // system will generate many IO events and this method will be called
56 // frequently. It will likely achieve max transactions quickly on its own.
57 if (getQueueCount() > 0) {
58 if (getTransactionCount() >= max_transactions_) {
60 DHCP_DDNS_AT_MAX_TRANSACTIONS).arg(getQueueCount())
61 .arg(getMaxTransactions());
62
63 return;
64 }
65
66 // We are not at maximum transactions, so pick and start the next job.
68 }
69}
70
71void
73 // Cycle through transaction list and do whatever needs to be done
74 // for finished transactions.
75 // At the moment all we do is remove them from the list. This is likely
76 // to expand as DHCP_DDNS matures.
77 // NOTE: One must use postfix increments of the iterator on the calls
78 // to erase. This replaces the old iterator which becomes invalid by the
79 // erase with a the next valid iterator. Prefix incrementing will not
80 // work.
81 TransactionList::iterator it = transaction_list_.begin();
82 while (it != transaction_list_.end()) {
83 NameChangeTransactionPtr trans = (*it).second;
84 if (trans->isModelDone()) {
85 // @todo Additional actions based on NCR status could be
86 // performed here.
87 transaction_list_.erase(it++);
88 } else {
89 ++it;
90 }
91 }
92}
93
95 // Start at the front of the queue, looking for the first entry for
96 // which no transaction is in progress. If we find an eligible entry
97 // remove it from the queue and make a transaction for it.
98 // Requests and transactions are associated by DHCID. If a request has
99 // the same DHCID as a transaction, they are presumed to be for the same
100 // "end user".
101 size_t queue_count = getQueueCount();
102 for (size_t index = 0; index < queue_count; ++index) {
103 dhcp_ddns::NameChangeRequestPtr found_ncr = queue_mgr_->peekAt(index);
104 if (!hasTransaction(found_ncr->getDhcid())) {
105 queue_mgr_->dequeueAt(index);
106 makeTransaction(found_ncr);
107 return;
108 }
109 }
110
111 // There were no eligible jobs. All of the current DHCIDs already have
112 // transactions pending.
114 DHCP_DDNS_NO_ELIGIBLE_JOBS)
115 .arg(getQueueCount()).arg(getTransactionCount());
116}
117
118void
120 // First lets ensure there is not a transaction in progress for this
121 // DHCID. (pickNextJob should ensure this, as it is the only real caller
122 // but for safety's sake we'll check).
123 const TransactionKey& key = next_ncr->getDhcid();
124 if (findTransaction(key) != transactionListEnd()) {
125 // This is programmatic error. Caller(s) should be checking this.
126 isc_throw(D2UpdateMgrError, "Transaction already in progress for: "
127 << key.toStr());
128 }
129
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);
137 DHCP_DDNS_FWD_REQUEST_IGNORED)
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) {
147 LOG_ERROR(dhcp_to_d2_logger, DHCP_DDNS_NO_FWD_MATCH_ERROR)
148 .arg(next_ncr->getRequestId())
149 .arg(next_ncr->toText());
150 return;
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);
163 DHCP_DDNS_REV_REQUEST_IGNORED)
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) {
173 LOG_ERROR(dhcp_to_d2_logger, DHCP_DDNS_NO_REV_MATCH_ERROR)
174 .arg(next_ncr->getRequestId())
175 .arg(next_ncr->toText());
176 return;
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) {
187 DHCP_DDNS_REQUEST_DROPPED)
188 .arg(next_ncr->getRequestId())
189 .arg(next_ncr->toText());
190 return;
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() == dhcp_ddns::CHG_ADD) {
200 trans.reset(new NameAddTransaction(io_service_, next_ncr,
201 forward_domain, reverse_domain,
202 cfg_mgr_));
203 } else {
204 trans.reset(new NameRemoveTransaction(io_service_, next_ncr,
205 forward_domain, reverse_domain,
206 cfg_mgr_));
207 }
208
209 // Add the new transaction to the list.
210 transaction_list_[key] = trans;
211
212 // Start it.
213 trans->startTransaction();
214}
215
216TransactionList::iterator
218 return (transaction_list_.find(key));
219}
220
221bool
223 return (findTransaction(key) != transactionListEnd());
224}
225
226void
228 TransactionList::iterator pos = findTransaction(key);
229 if (pos != transactionListEnd()) {
230 transaction_list_.erase(pos);
231 }
232}
233
234TransactionList::iterator
236 return (transaction_list_.begin());
237}
238
239TransactionList::iterator
241 return (transaction_list_.end());
242}
243
244void
246 // @todo for now this just wipes them out. We might need something
247 // more elegant, that allows a cancel first.
248 transaction_list_.clear();
249}
250
251void
252D2UpdateMgr::setMaxTransactions(const size_t new_trans_max) {
253 // Obviously we need at room for at least one transaction.
254 if (new_trans_max < 1) {
255 isc_throw(D2UpdateMgrError, "D2UpdateMgr"
256 " maximum transactions limit must be greater than zero");
257 }
258
259 // Do not allow the list maximum to be set to less then current list size.
260 if (new_trans_max < getTransactionCount()) {
261 isc_throw(D2UpdateMgrError, "D2UpdateMgr maximum transaction limit "
262 "cannot be less than the current transaction count :"
264 }
265
266 max_transactions_ = new_trans_max;
267}
268
269size_t
271 return (queue_mgr_->getQueueSize());
272}
273
274size_t
276 return (transaction_list_.size());
277}
278
279
280} // namespace isc::d2
281} // namespace isc
Thrown if the update manager encounters a general error.
Definition: d2_update_mgr.h:27
TransactionList::iterator transactionListEnd()
Returns the transaction list end position.
bool hasTransaction(const TransactionKey &key)
Convenience method that checks transaction list for the given key.
TransactionList::iterator findTransaction(const TransactionKey &key)
Search the transaction list for the given key.
virtual ~D2UpdateMgr()
Destructor.
void removeTransaction(const TransactionKey &key)
Removes the entry pointed to by key from the transaction list.
void makeTransaction(isc::dhcp_ddns::NameChangeRequestPtr &ncr)
Create a new transaction for the given request.
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.
TransactionList::iterator transactionListBegin()
Returns the transaction list beg position.
void clearTransactionList()
Immediately discards all entries in the transaction list.
void sweep()
Check current transactions; start transactions for new requests.
size_t getMaxTransactions() const
Returns the maximum number of concurrent transactions.
size_t getQueueCount() const
Convenience method that returns the number of requests queued.
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.
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
Embodies the "life-cycle" required to carry out a DDNS Add update.
Definition: nc_add.h:52
Embodies the "life-cycle" required to carry out a DDNS Remove update.
Definition: nc_remove.h:51
Container class for handling the DHCID value within a NameChangeRequest.
Definition: ncr_msg.h:86
std::string toStr() const
Returns the DHCID value as a string of hexadecimal digits.
Definition: ncr_msg.cc:96
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
boost::shared_ptr< DdnsDomain > DdnsDomainPtr
Defines a pointer for DdnsDomain instances.
Definition: d2_config.h:586
boost::shared_ptr< D2CfgMgr > D2CfgMgrPtr
Defines a shared pointer to D2CfgMgr.
Definition: d2_cfg_mgr.h:297
isc::log::Logger dhcp_to_d2_logger("dhcp-to-d2")
Definition: d2_log.h:19
boost::shared_ptr< NameChangeTransaction > NameChangeTransactionPtr
Defines a pointer to a NameChangeTransaction.
Definition: nc_trans.h:588
boost::shared_ptr< D2QueueMgr > D2QueueMgrPtr
Defines a pointer for manager instances.
Definition: d2_queue_mgr.h:343
boost::shared_ptr< NameChangeRequest > NameChangeRequestPtr
Defines a pointer to a NameChangeRequest.
Definition: ncr_msg.h:214
const int DBGLVL_TRACE_DETAIL_DATA
Trace data associated with detailed operations.
Definition: log_dbglevels.h:74
Defines the logger used by the top-level component of kea-dhcp-ddns.
This file defines the class NameAddTransaction.
This file defines the class NameRemoveTransaction.