d2_update_mgr.cc 9.7 KB

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