d2_update_mgr.cc 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  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. #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, isc::log::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 Additional 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, isc::log::DBGLVL_TRACE_DETAIL_DATA,
  97. DHCP_DDNS_NO_ELIGIBLE_JOBS)
  98. .arg(getQueueCount()).arg(getTransactionCount());
  99. }
  100. void
  101. D2UpdateMgr::makeTransaction(dhcp_ddns::NameChangeRequestPtr& next_ncr) {
  102. // First lets ensure there is not a transaction in progress for this
  103. // DHCID. (pickNextJob should ensure this, as it is the only real caller
  104. // but for safety's sake we'll check).
  105. const TransactionKey& key = next_ncr->getDhcid();
  106. if (findTransaction(key) != transactionListEnd()) {
  107. // This is programmatic error. Caller(s) should be checking this.
  108. isc_throw(D2UpdateMgrError, "Transaction already in progress for: "
  109. << key.toStr());
  110. }
  111. int direction_count = 0;
  112. // If forward change is enabled, match to forward servers.
  113. DdnsDomainPtr forward_domain;
  114. if (next_ncr->isForwardChange()) {
  115. if (!cfg_mgr_->forwardUpdatesEnabled()) {
  116. next_ncr->setForwardChange(false);
  117. LOG_DEBUG(dhcp_to_d2_logger, isc::log::DBGLVL_TRACE_DETAIL_DATA,
  118. DHCP_DDNS_FWD_REQUEST_IGNORED)
  119. .arg(next_ncr->getRequestId())
  120. .arg(next_ncr->toText());
  121. } else {
  122. bool matched = cfg_mgr_->matchForward(next_ncr->getFqdn(),
  123. forward_domain);
  124. // Could not find a match for forward DNS server. Log it and get
  125. // out. This has the net affect of dropping the request on the
  126. // floor.
  127. if (!matched) {
  128. LOG_ERROR(dhcp_to_d2_logger, DHCP_DDNS_NO_FWD_MATCH_ERROR)
  129. .arg(next_ncr->getRequestId())
  130. .arg(next_ncr->toText());
  131. return;
  132. }
  133. ++direction_count;
  134. }
  135. }
  136. // If reverse change is enabled, match to reverse servers.
  137. DdnsDomainPtr reverse_domain;
  138. if (next_ncr->isReverseChange()) {
  139. if (!cfg_mgr_->reverseUpdatesEnabled()) {
  140. next_ncr->setReverseChange(false);
  141. LOG_DEBUG(dhcp_to_d2_logger, isc::log::DBGLVL_TRACE_DETAIL_DATA,
  142. DHCP_DDNS_REV_REQUEST_IGNORED)
  143. .arg(next_ncr->getRequestId())
  144. .arg(next_ncr->toText());
  145. } else {
  146. bool matched = cfg_mgr_->matchReverse(next_ncr->getIpAddress(),
  147. reverse_domain);
  148. // Could not find a match for reverse DNS server. Log it and get
  149. // out. This has the net affect of dropping the request on the
  150. // floor.
  151. if (!matched) {
  152. LOG_ERROR(dhcp_to_d2_logger, DHCP_DDNS_NO_REV_MATCH_ERROR)
  153. .arg(next_ncr->getRequestId())
  154. .arg(next_ncr->toText());
  155. return;
  156. }
  157. ++direction_count;
  158. }
  159. }
  160. // If there is nothing to actually do, then the request falls on the floor.
  161. // Should we log this?
  162. if (!direction_count) {
  163. LOG_DEBUG(dhcp_to_d2_logger, isc::log::DBGLVL_TRACE_DETAIL_DATA,
  164. DHCP_DDNS_REQUEST_DROPPED)
  165. .arg(next_ncr->getRequestId())
  166. .arg(next_ncr->toText());
  167. return;
  168. }
  169. // We matched to the required servers, so construct the transaction.
  170. // @todo If multi-threading is implemented, one would pass in an
  171. // empty IOServicePtr, rather than our instance value. This would cause
  172. // the transaction to instantiate its own, separate IOService to handle
  173. // the transaction's IO.
  174. NameChangeTransactionPtr trans;
  175. if (next_ncr->getChangeType() == dhcp_ddns::CHG_ADD) {
  176. trans.reset(new NameAddTransaction(io_service_, next_ncr,
  177. forward_domain, reverse_domain,
  178. cfg_mgr_));
  179. } else {
  180. trans.reset(new NameRemoveTransaction(io_service_, next_ncr,
  181. forward_domain, reverse_domain,
  182. cfg_mgr_));
  183. }
  184. // Add the new transaction to the list.
  185. transaction_list_[key] = trans;
  186. // Start it.
  187. trans->startTransaction();
  188. }
  189. TransactionList::iterator
  190. D2UpdateMgr::findTransaction(const TransactionKey& key) {
  191. return (transaction_list_.find(key));
  192. }
  193. bool
  194. D2UpdateMgr::hasTransaction(const TransactionKey& key) {
  195. return (findTransaction(key) != transactionListEnd());
  196. }
  197. void
  198. D2UpdateMgr::removeTransaction(const TransactionKey& key) {
  199. TransactionList::iterator pos = findTransaction(key);
  200. if (pos != transactionListEnd()) {
  201. transaction_list_.erase(pos);
  202. }
  203. }
  204. TransactionList::iterator
  205. D2UpdateMgr::transactionListBegin() {
  206. return (transaction_list_.begin());
  207. }
  208. TransactionList::iterator
  209. D2UpdateMgr::transactionListEnd() {
  210. return (transaction_list_.end());
  211. }
  212. void
  213. D2UpdateMgr::clearTransactionList() {
  214. // @todo for now this just wipes them out. We might need something
  215. // more elegant, that allows a cancel first.
  216. transaction_list_.clear();
  217. }
  218. void
  219. D2UpdateMgr::setMaxTransactions(const size_t new_trans_max) {
  220. // Obviously we need at room for at least one transaction.
  221. if (new_trans_max < 1) {
  222. isc_throw(D2UpdateMgrError, "D2UpdateMgr"
  223. " maximum transactions limit must be greater than zero");
  224. }
  225. // Do not allow the list maximum to be set to less then current list size.
  226. if (new_trans_max < getTransactionCount()) {
  227. isc_throw(D2UpdateMgrError, "D2UpdateMgr maximum transaction limit "
  228. "cannot be less than the current transaction count :"
  229. << getTransactionCount());
  230. }
  231. max_transactions_ = new_trans_max;
  232. }
  233. size_t
  234. D2UpdateMgr::getQueueCount() const {
  235. return (queue_mgr_->getQueueSize());
  236. }
  237. size_t
  238. D2UpdateMgr::getTransactionCount() const {
  239. return (transaction_list_.size());
  240. }
  241. } // namespace isc::d2
  242. } // namespace isc