Kea  1.9.9-git
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 
19 namespace isc {
20 namespace d2 {
21 
23 
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_) {
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 
46  transaction_list_.clear();
47 }
48 
50  // cleanup finished transactions;
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_) {
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 
73 void
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 
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.
117  .arg(getQueueCount()).arg(getTransactionCount());
118 }
119 
120 void
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);
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) {
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);
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) {
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) {
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.
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 
230 TransactionList::iterator
232  return (transaction_list_.find(key));
233 }
234 
235 bool
237  return (findTransaction(key) != transactionListEnd());
238 }
239 
240 void
242  TransactionList::iterator pos = findTransaction(key);
243  if (pos != transactionListEnd()) {
244  transaction_list_.erase(pos);
245  }
246 }
247 
248 TransactionList::iterator
250  return (transaction_list_.begin());
251 }
252 
253 TransactionList::iterator
255  return (transaction_list_.end());
256 }
257 
258 void
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 
265 void
266 D2UpdateMgr::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 
283 size_t
285  return (queue_mgr_->getQueueSize());
286 }
287 
288 size_t
290  return (transaction_list_.size());
291 }
292 
293 
294 } // namespace isc::d2
295 } // namespace isc
void clearTransactionList()
Immediately discards all entries in the transaction list.
std::string toStr() const
Returns the DHCID value as a string of hexadecimal digits.
Definition: ncr_msg.cc:95
Embodies the "life-cycle" required to carry out a DDNS Add update.
Definition: simple_add.h:47
boost::shared_ptr< NameChangeTransaction > NameChangeTransactionPtr
Defines a pointer to a NameChangeTransaction.
Definition: nc_trans.h:588
boost::shared_ptr< DdnsDomain > DdnsDomainPtr
Defines a pointer for DdnsDomain instances.
Definition: d2_config.h:591
const isc::log::MessageID DHCP_DDNS_REV_REQUEST_IGNORED
Definition: d2_messages.h:76
This file defines the class NameAddTransaction.
virtual ~D2UpdateMgr()
Destructor.
void makeTransaction(isc::dhcp_ddns::NameChangeRequestPtr &ncr)
Create a new transaction for the given request.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
const isc::log::MessageID DHCP_DDNS_REQUEST_DROPPED
Definition: d2_messages.h:65
void sweep()
Check current transactions; start transactions for new requests.
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.
size_t getQueueCount() const
Convenience method that returns the number of requests queued.
TransactionList::iterator transactionListBegin()
Returns the transaction list beg position.
boost::shared_ptr< NameChangeRequest > NameChangeRequestPtr
Defines a pointer to a NameChangeRequest.
Definition: ncr_msg.h:212
void removeTransaction(const TransactionKey &key)
Removes the entry pointed to by key from the transaction list.
void pickNextJob()
Starts a transaction for the next eligible request in the queue.
D2UpdateMgr(D2QueueMgrPtr &queue_mgr, D2CfgMgrPtr &cfg_mgr, asiolink::IOServicePtr &io_service, const size_t max_transactions=MAX_TRANSACTIONS_DEFAULT)
Constructor.
boost::shared_ptr< D2QueueMgr > D2QueueMgrPtr
Defines a pointer for manager instances.
Definition: d2_queue_mgr.h:343
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
size_t getTransactionCount() const
Returns the current number of transactions.
Embodies the "life-cycle" required to carry out a DDNS Remove update.
Definition: nc_remove.h:51
const int DBGLVL_TRACE_DETAIL_DATA
Trace data associated with detailed operations.
Definition: log_dbglevels.h:74
const isc::log::MessageID DHCP_DDNS_NO_REV_MATCH_ERROR
Definition: d2_messages.h:47
isc::log::Logger dhcp_to_d2_logger("dhcp-to-d2")
Definition: d2_log.h:19
boost::shared_ptr< D2CfgMgr > D2CfgMgrPtr
Defines a shared pointer to D2CfgMgr.
Definition: d2_cfg_mgr.h:334
size_t getMaxTransactions() const
Returns the maximum number of concurrent transactions.
This file defines the class NameRemoveTransaction.
const isc::log::MessageID DHCP_DDNS_NO_FWD_MATCH_ERROR
Definition: d2_messages.h:45
TransactionList::iterator transactionListEnd()
Returns the transaction list end position.
const isc::log::MessageID DHCP_DDNS_AT_MAX_TRANSACTIONS
Definition: d2_messages.h:14
Embodies the "life-cycle" required to carry out a DDNS Add update.
Definition: nc_add.h:52
Defines the logger used by the top-level component of kea-dhcp-ddns.
void checkFinishedTransactions()
Performs post-completion cleanup on completed transactions.
Container class for handling the DHCID value within a NameChangeRequest.
Definition: ncr_msg.h:86
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
Thrown if the update manager encounters a general error.
Definition: d2_update_mgr.h:27
Embodies the "life-cycle" required to carry out a DDNS Remove update.
Definition: simple_remove.h:49
This file defines the class D2UpdateMgr.
void setMaxTransactions(const size_t max_transactions)
Sets the maximum number of entries allowed 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
const isc::log::MessageID DHCP_DDNS_FWD_REQUEST_IGNORED
Definition: d2_messages.h:41
const isc::log::MessageID DHCP_DDNS_NO_ELIGIBLE_JOBS
Definition: d2_messages.h:44