d2_update_mgr.cc 9.8 KB

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