d2_update_mgr.cc 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. // Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <d2/d2_update_mgr.h>
  15. #include <sstream>
  16. #include <iostream>
  17. #include <vector>
  18. namespace isc {
  19. namespace d2 {
  20. const size_t D2UpdateMgr::MAX_TRANSACTIONS_DEFAULT;
  21. D2UpdateMgr::D2UpdateMgr(D2QueueMgrPtr& queue_mgr, D2CfgMgrPtr& cfg_mgr,
  22. IOServicePtr& io_service,
  23. const size_t max_transactions)
  24. :queue_mgr_(queue_mgr), cfg_mgr_(cfg_mgr), io_service_(io_service) {
  25. if (!queue_mgr_) {
  26. isc_throw(D2UpdateMgrError, "D2UpdateMgr queue manager cannot be null");
  27. }
  28. if (!cfg_mgr_) {
  29. isc_throw(D2UpdateMgrError,
  30. "D2UpdateMgr configuration manager cannot be null");
  31. }
  32. if (!io_service_) {
  33. isc_throw(D2UpdateMgrError, "IOServicePtr cannot be null");
  34. }
  35. // Use setter to do validation.
  36. setMaxTransactions(max_transactions);
  37. }
  38. D2UpdateMgr::~D2UpdateMgr() {
  39. transaction_list_.clear();
  40. }
  41. void D2UpdateMgr::sweep() {
  42. // cleanup finished transactions;
  43. checkFinishedTransactions();
  44. // if the queue isn't empty, find the next suitable job and
  45. // start a transaction for it.
  46. // @todo - Do we want to queue max transactions? The logic here will only
  47. // start one new transaction per invocation. On the other hand a busy
  48. // system will generate many IO events and this method will be called
  49. // frequently. It will likely achieve max transactions quickly on its own.
  50. if (getQueueCount() > 0) {
  51. if (getTransactionCount() >= max_transactions_) {
  52. LOG_DEBUG(dctl_logger, DBGLVL_TRACE_DETAIL_DATA,
  53. DHCP_DDNS_AT_MAX_TRANSACTIONS).arg(getQueueCount())
  54. .arg(getMaxTransactions());
  55. return;
  56. }
  57. // We are not at maximum transactions, so pick and start the next job.
  58. pickNextJob();
  59. }
  60. }
  61. void
  62. D2UpdateMgr::checkFinishedTransactions() {
  63. // Cycle through transaction list and do whatever needs to be done
  64. // for finished transactions.
  65. // At the moment all we do is remove them from the list. This is likely
  66. // to expand as DHCP_DDNS matures.
  67. // NOTE: One must use postfix increments of the iterator on the calls
  68. // to erase. This replaces the old iterator which becomes invalid by the
  69. // erase with a the next valid iterator. Prefix incrementing will not
  70. // work.
  71. TransactionList::iterator it = transaction_list_.begin();
  72. while (it != transaction_list_.end()) {
  73. NameChangeTransactionPtr trans = (*it).second;
  74. switch (trans->getNcrStatus()) {
  75. case dhcp_ddns::ST_COMPLETED:
  76. transaction_list_.erase(it++);
  77. break;
  78. case dhcp_ddns::ST_FAILED:
  79. transaction_list_.erase(it++);
  80. break;
  81. default:
  82. ++it;
  83. break;
  84. }
  85. }
  86. }
  87. void D2UpdateMgr::pickNextJob() {
  88. // Start at the front of the queue, looking for the first entry for
  89. // which no transaction is in progress. If we find an eligible entry
  90. // remove it from the queue and make a transaction for it.
  91. // Requests and transactions are associated by DHCID. If a request has
  92. // the same DHCID as a transaction, they are presumed to be for the same
  93. // "end user".
  94. size_t queue_count = getQueueCount();
  95. for (size_t index = 0; index < queue_count; ++index) {
  96. dhcp_ddns::NameChangeRequestPtr found_ncr = queue_mgr_->peekAt(index);
  97. if (!hasTransaction(found_ncr->getDhcid())) {
  98. queue_mgr_->dequeueAt(index);
  99. makeTransaction(found_ncr);
  100. return;
  101. }
  102. }
  103. // There were no eligible jobs. All of the current DHCIDs already have
  104. // transactions pending.
  105. LOG_DEBUG(dctl_logger, DBGLVL_TRACE_DETAIL_DATA, DHCP_DDNS_NO_ELIGIBLE_JOBS)
  106. .arg(getQueueCount()).arg(getTransactionCount());
  107. }
  108. void
  109. D2UpdateMgr::makeTransaction(dhcp_ddns::NameChangeRequestPtr& next_ncr) {
  110. // First lets ensure there is not a transaction in progress for this
  111. // DHCID. (pickNextJob should ensure this, as it is the only real caller
  112. // but for safety's sake we'll check).
  113. const TransactionKey& key = next_ncr->getDhcid();
  114. if (findTransaction(key) != transactionListEnd()) {
  115. // This is programmatic error. Caller(s) should be checking this.
  116. isc_throw(D2UpdateMgrError, "Transaction already in progress for: "
  117. << key.toStr());
  118. }
  119. // If forward change is enabled, match to forward servers.
  120. DdnsDomainPtr forward_domain;
  121. if (next_ncr->isForwardChange()) {
  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 out.
  125. // This has the net affect of dropping the request on the floor.
  126. if (!matched) {
  127. LOG_ERROR(dctl_logger, DHCP_DDNS_NO_FWD_MATCH_ERROR)
  128. .arg(next_ncr->getFqdn());
  129. return;
  130. }
  131. }
  132. // If reverse change is enabled, match to reverse servers.
  133. DdnsDomainPtr reverse_domain;
  134. if (next_ncr->isReverseChange()) {
  135. bool matched = cfg_mgr_->matchReverse(next_ncr->getIpAddress(),
  136. reverse_domain);
  137. // Could not find a match for reverse DNS server. Log it and get out.
  138. // This has the net affect of dropping the request on the floor.
  139. if (!matched) {
  140. LOG_ERROR(dctl_logger, DHCP_DDNS_NO_REV_MATCH_ERROR)
  141. .arg(next_ncr->getIpAddress());
  142. return;
  143. }
  144. }
  145. // We matched to the required servers, so construct the transaction.
  146. NameChangeTransactionPtr trans(new NameChangeTransaction(io_service_,
  147. next_ncr,
  148. forward_domain,
  149. reverse_domain));
  150. // Add the new transaction to the list.
  151. transaction_list_[key] = trans;
  152. }
  153. TransactionList::iterator
  154. D2UpdateMgr::findTransaction(const TransactionKey& key) {
  155. return (transaction_list_.find(key));
  156. }
  157. bool
  158. D2UpdateMgr::hasTransaction(const TransactionKey& key) {
  159. return (findTransaction(key) != transactionListEnd());
  160. }
  161. void
  162. D2UpdateMgr::removeTransaction(const TransactionKey& key) {
  163. TransactionList::iterator pos = findTransaction(key);
  164. if (pos != transactionListEnd()) {
  165. transaction_list_.erase(pos);
  166. }
  167. }
  168. TransactionList::iterator
  169. D2UpdateMgr::transactionListEnd() {
  170. return (transaction_list_.end());
  171. }
  172. void
  173. D2UpdateMgr::clearTransactionList() {
  174. // @todo for now this just wipes them out. We might need something
  175. // more elegant, that allows a cancel first.
  176. transaction_list_.clear();
  177. }
  178. void
  179. D2UpdateMgr::setMaxTransactions(const size_t new_trans_max) {
  180. // Obviously we need at room for at least one transaction.
  181. if (new_trans_max < 1) {
  182. isc_throw(D2UpdateMgrError, "D2UpdateMgr"
  183. " maximum transactions limit must be greater than zero");
  184. }
  185. // Do not allow the list maximum to be set to less then current list size.
  186. if (new_trans_max < getTransactionCount()) {
  187. isc_throw(D2UpdateMgrError, "D2UpdateMgr maximum transaction limit "
  188. "cannot be less than the current transaction count :"
  189. << getTransactionCount());
  190. }
  191. max_transactions_ = new_trans_max;
  192. }
  193. size_t
  194. D2UpdateMgr::getQueueCount() const {
  195. return (queue_mgr_->getQueueSize());
  196. }
  197. size_t
  198. D2UpdateMgr::getTransactionCount() const {
  199. return (transaction_list_.size());
  200. }
  201. } // namespace isc::d2
  202. } // namespace isc