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 
17 namespace isc {
18 namespace 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.
67  pickNextJob();
68  }
69 }
70 
71 void
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 
118 void
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 
216 TransactionList::iterator
218  return (transaction_list_.find(key));
219 }
220 
221 bool
223  return (findTransaction(key) != transactionListEnd());
224 }
225 
226 void
228  TransactionList::iterator pos = findTransaction(key);
229  if (pos != transactionListEnd()) {
230  transaction_list_.erase(pos);
231  }
232 }
233 
234 TransactionList::iterator
236  return (transaction_list_.begin());
237 }
238 
239 TransactionList::iterator
241  return (transaction_list_.end());
242 }
243 
244 void
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 
251 void
252 D2UpdateMgr::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 :"
263  << getTransactionCount());
264  }
265 
266  max_transactions_ = new_trans_max;
267 }
268 
269 size_t
271  return (queue_mgr_->getQueueSize());
272 }
273 
274 size_t
276  return (transaction_list_.size());
277 }
278 
279 
280 } // namespace isc::d2
281 } // namespace isc
isc::dhcp_ddns::D2Dhcid::toStr
std::string toStr() const
Returns the DHCID value as a string of hexadecimal digits.
Definition: ncr_msg.cc:96
isc::d2::D2UpdateMgrError
Thrown if the update manager encounters a general error.
Definition: d2_update_mgr.h:27
isc::d2::D2UpdateMgr::makeTransaction
void makeTransaction(isc::dhcp_ddns::NameChangeRequestPtr &ncr)
Create a new transaction for the given request.
Definition: d2_update_mgr.cc:119
isc::d2::D2UpdateMgr::transactionListEnd
TransactionList::iterator transactionListEnd()
Returns the transaction list end position.
Definition: d2_update_mgr.cc:240
LOG_ERROR
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
isc::d2::NameChangeTransactionPtr
boost::shared_ptr< NameChangeTransaction > NameChangeTransactionPtr
Defines a pointer to a NameChangeTransaction.
Definition: nc_trans.h:588
isc::d2::NameAddTransaction
Embodies the "life-cycle" required to carry out a DDNS Add update.
Definition: nc_add.h:52
isc::d2::D2UpdateMgr::checkFinishedTransactions
void checkFinishedTransactions()
Performs post-completion cleanup on completed transactions.
Definition: d2_update_mgr.cc:72
isc::d2::D2CfgMgrPtr
boost::shared_ptr< D2CfgMgr > D2CfgMgrPtr
Defines a shared pointer to D2CfgMgr.
Definition: d2_cfg_mgr.h:297
d2_update_mgr.h
isc::d2::D2UpdateMgr::getMaxTransactions
size_t getMaxTransactions() const
Returns the maximum number of concurrent transactions.
Definition: d2_update_mgr.h:173
isc::d2::D2QueueMgrPtr
boost::shared_ptr< D2QueueMgr > D2QueueMgrPtr
Defines a pointer for manager instances.
Definition: d2_queue_mgr.h:343
isc::dhcp_ddns::NameChangeRequestPtr
boost::shared_ptr< NameChangeRequest > NameChangeRequestPtr
Defines a pointer to a NameChangeRequest.
Definition: ncr_msg.h:212
isc::dhcp_ddns::D2Dhcid
Container class for handling the DHCID value within a NameChangeRequest.
Definition: ncr_msg.h:86
isc::d2::D2UpdateMgr::clearTransactionList
void clearTransactionList()
Immediately discards all entries in the transaction list.
Definition: d2_update_mgr.cc:245
isc::d2::D2UpdateMgr::findTransaction
TransactionList::iterator findTransaction(const TransactionKey &key)
Search the transaction list for the given key.
Definition: d2_update_mgr.cc:217
isc::d2::NameRemoveTransaction
Embodies the "life-cycle" required to carry out a DDNS Remove update.
Definition: nc_remove.h:51
nc_remove.h
isc
Defines the logger used by the top-level component of kea-dhcp-ddns.
Definition: agent_parser.cc:144
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition: exceptions/exceptions.h:192
isc::d2::D2UpdateMgr::getTransactionCount
size_t getTransactionCount() const
Returns the current number of transactions.
Definition: d2_update_mgr.cc:275
LOG_DEBUG
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
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:252
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:22
isc::dhcp_ddns::CHG_ADD
@ CHG_ADD
Definition: ncr_msg.h:47
isc::d2::D2UpdateMgr::hasTransaction
bool hasTransaction(const TransactionKey &key)
Convenience method that checks transaction list for the given key.
Definition: d2_update_mgr.cc:222
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::D2UpdateMgr::removeTransaction
void removeTransaction(const TransactionKey &key)
Removes the entry pointed to by key from the transaction list.
Definition: d2_update_mgr.cc:227
isc::d2::dhcp_to_d2_logger
isc::log::Logger dhcp_to_d2_logger("dhcp-to-d2")
Definition: d2_log.h:19
isc::d2::D2UpdateMgr::sweep
void sweep()
Check current transactions; start transactions for new requests.
Definition: d2_update_mgr.cc:47
isc::log::DBGLVL_TRACE_DETAIL_DATA
const int DBGLVL_TRACE_DETAIL_DATA
Trace data associated with detailed operations.
Definition: log_dbglevels.h:74
nc_add.h
isc::d2::D2UpdateMgr::getQueueCount
size_t getQueueCount() const
Convenience method that returns the number of requests queued.
Definition: d2_update_mgr.cc:270
isc::d2::D2UpdateMgr::transactionListBegin
TransactionList::iterator transactionListBegin()
Returns the transaction list beg position.
Definition: d2_update_mgr.cc:235
isc::d2::D2UpdateMgr::~D2UpdateMgr
virtual ~D2UpdateMgr()
Destructor.
Definition: d2_update_mgr.cc:43
isc::d2::D2UpdateMgr::pickNextJob
void pickNextJob()
Starts a transaction for the next eligible request in the queue.
Definition: d2_update_mgr.cc:94
isc::d2::DdnsDomainPtr
boost::shared_ptr< DdnsDomain > DdnsDomainPtr
Defines a pointer for DdnsDomain instances.
Definition: d2_config.h:586