d2_queue_mgr.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  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. #ifndef D2_QUEUE_MGR_H
  7. #define D2_QUEUE_MGR_H
  8. /// @file d2_queue_mgr.h This file defines the class D2QueueMgr.
  9. #include <asiolink/io_service.h>
  10. #include <exceptions/exceptions.h>
  11. #include <dhcp_ddns/ncr_msg.h>
  12. #include <dhcp_ddns/ncr_io.h>
  13. #include <boost/noncopyable.hpp>
  14. #include <deque>
  15. namespace isc {
  16. namespace d2 {
  17. /// @brief Defines a queue of requests.
  18. /// @todo This may be replaced with an actual class in the future.
  19. typedef std::deque<dhcp_ddns::NameChangeRequestPtr> RequestQueue;
  20. /// @brief Thrown if the queue manager encounters a general error.
  21. class D2QueueMgrError : public isc::Exception {
  22. public:
  23. D2QueueMgrError(const char* file, size_t line, const char* what) :
  24. isc::Exception(file, line, what) { };
  25. };
  26. /// @brief Thrown if the queue manager's receive handler is passed
  27. /// a failure result.
  28. class D2QueueMgrReceiveError : public isc::Exception {
  29. public:
  30. D2QueueMgrReceiveError(const char* file, size_t line, const char* what) :
  31. isc::Exception(file, line, what) { };
  32. };
  33. /// @brief Thrown if the request queue is full when an enqueue is attempted.
  34. class D2QueueMgrQueueFull : public isc::Exception {
  35. public:
  36. D2QueueMgrQueueFull(const char* file, size_t line, const char* what) :
  37. isc::Exception(file, line, what) { };
  38. };
  39. /// @brief Thrown if the request queue empty and a read is attempted.
  40. class D2QueueMgrQueueEmpty : public isc::Exception {
  41. public:
  42. D2QueueMgrQueueEmpty(const char* file, size_t line, const char* what) :
  43. isc::Exception(file, line, what) { };
  44. };
  45. /// @brief Thrown if a queue index is beyond the end of the queue
  46. class D2QueueMgrInvalidIndex : public isc::Exception {
  47. public:
  48. D2QueueMgrInvalidIndex(const char* file, size_t line, const char* what) :
  49. isc::Exception(file, line, what) { };
  50. };
  51. /// @brief D2QueueMgr creates and manages a queue of DNS update requests.
  52. ///
  53. /// D2QueueMgr is a class specifically designed as an integral part of DHCP-DDNS.
  54. /// Its primary responsibility is to listen for NameChangeRequests from
  55. /// DHCP-DDNS clients (e.g. DHCP servers) and queue them for processing. In
  56. /// addition it may provide a number of services to locate entries in the queue
  57. /// such as by FQDN or DHCID. These services may eventually be used
  58. /// for processing optimization. The initial implementation will support
  59. /// simple FIFO access.
  60. ///
  61. /// D2QueueMgr uses a NameChangeListener to asynchronously receive requests.
  62. /// It derives from NameChangeListener::RequestReceiveHandler and supplies an
  63. /// implementation of the operator()(Result, NameChangeRequestPtr). It is
  64. /// through this operator() that D2QueueMgr is passed inbound NCRs. D2QueueMgr
  65. /// will add each newly received request onto the back of the request queue
  66. ///
  67. /// D2QueueMgr defines a simple state model constructed around the status of
  68. /// its NameChangeListener, consisting of the following states:
  69. ///
  70. /// * NOT_INITTED - D2QueueMgr has been constructed, but its listener has
  71. /// not been initialized.
  72. ///
  73. /// * INITTED - The listener has been initialized, but it is not open for
  74. /// listening. To move from NOT_INITTED to INITTED, one of the D2QueueMgr
  75. /// listener initialization methods must be invoked. Currently there is
  76. /// only one type of listener, NameChangeUDPListener, hence there is only
  77. /// one listener initialization method, initUDPListener. As more listener
  78. /// types are created, listener initialization methods will need to be
  79. /// added.
  80. ///
  81. /// * RUNNING - The listener is open and listening for requests.
  82. /// Once initialized, in order to begin listening for requests, the
  83. /// startListener() method must be invoked. Upon successful completion of
  84. /// of this call, D2QueueMgr will begin receiving requests as they arrive
  85. /// without any further steps. This method may be called from the INITTED
  86. /// or one of the STOPPED states.
  87. ///
  88. /// * STOPPING - The listener is in the process of stopping active
  89. /// listening. This is transitory state between RUNNING and STOPPED, which
  90. /// is completed by IO cancellation event.
  91. ///
  92. /// * STOPPED - The listener has been listening but has been stopped
  93. /// without error. To return to listening, startListener() must be invoked.
  94. ///
  95. /// * STOPPED_QUEUE_FULL - Request queue is full, the listener has been
  96. /// stopped. D2QueueMgr will enter this state when the request queue
  97. /// reaches the maximum queue size. Once this limit is reached, the
  98. /// listener will be closed and no further requests will be received.
  99. /// To return to listening, startListener() must be invoked. Note that so
  100. /// long as the queue is full, any attempt to queue a request will fail.
  101. ///
  102. /// * STOPPED_RECV_ERROR - The listener has experienced a receive error
  103. /// and has been stopped. D2QueueMgr will enter this state when it is
  104. /// passed a failed status into the request completion handler. To return
  105. /// to listening, startListener() must be invoked.
  106. ///
  107. /// D2QueueMgr does not attempt to recover from stopped conditions, this is left
  108. /// to upper layers.
  109. ///
  110. /// It is important to note that the queue contents are preserved between
  111. /// state transitions. In other words entries in the queue remain there
  112. /// until they are removed explicitly via the deque() or implicitly by
  113. /// via the clearQueue() method.
  114. ///
  115. class D2QueueMgr : public dhcp_ddns::NameChangeListener::RequestReceiveHandler,
  116. boost::noncopyable {
  117. public:
  118. /// @brief Maximum number of entries allowed in the request queue.
  119. /// NOTE that 1024 is an arbitrary choice picked for the initial
  120. /// implementation.
  121. static const size_t MAX_QUEUE_DEFAULT = 1024;
  122. /// @brief Defines the list of possible states for D2QueueMgr.
  123. enum State {
  124. NOT_INITTED,
  125. INITTED,
  126. RUNNING,
  127. STOPPING,
  128. STOPPED_QUEUE_FULL,
  129. STOPPED_RECV_ERROR,
  130. STOPPED,
  131. };
  132. /// @brief Constructor
  133. ///
  134. /// Creates a D2QueueMgr instance. Note that the listener is not created
  135. /// in the constructor. The initial state will be NOT_INITTED.
  136. ///
  137. /// @param io_service IOService instance to be passed into the listener for
  138. /// IO management.
  139. /// @param max_queue_size the maximum number of entries allowed in the
  140. /// queue.
  141. /// This value must be greater than zero. It defaults to MAX_QUEUE_DEFAULT.
  142. ///
  143. /// @throw D2QueueMgrError if max_queue_size is zero.
  144. D2QueueMgr(asiolink::IOServicePtr& io_service,
  145. const size_t max_queue_size = MAX_QUEUE_DEFAULT);
  146. /// @brief Destructor
  147. virtual ~D2QueueMgr();
  148. /// @brief Initializes the listener as a UDP listener.
  149. ///
  150. /// Instantiates the listener_ member as NameChangeUDPListener passing
  151. /// the given parameters. Upon successful completion, the D2QueueMgr state
  152. /// will be INITTED.
  153. ///
  154. /// @param ip_address is the network address on which to listen
  155. /// @param port is the IP port on which to listen
  156. /// @param format is the wire format of the inbound requests.
  157. /// @param reuse_address enables IP address sharing when true
  158. /// It defaults to false.
  159. void initUDPListener(const isc::asiolink::IOAddress& ip_address,
  160. const uint32_t port,
  161. const dhcp_ddns::NameChangeFormat format,
  162. const bool reuse_address = false);
  163. /// @brief Starts actively listening for requests.
  164. ///
  165. /// Invokes the listener's startListening method passing in our
  166. /// IOService instance.
  167. ///
  168. /// @throw D2QueueMgrError if the listener has not been initialized,
  169. /// state is already RUNNING, or the listener fails to actually start.
  170. void startListening();
  171. /// @brief Function operator implementing the NCR receive callback.
  172. ///
  173. /// This method is invoked by the listener as part of its receive
  174. /// completion callback and is how the inbound NameChangeRequests are
  175. /// passed up to the D2QueueMgr for queueing.
  176. /// If the given result indicates a successful receive completion and
  177. /// there is room left in the queue, the given request is queued.
  178. ///
  179. /// If the queue is at maximum capacity, stopListening() is invoked and
  180. /// the state is set to STOPPED_QUEUE_FULL.
  181. ///
  182. /// If the result indicates IO stopped, then the state is set to STOPPED.
  183. /// Note this is not an error, it results from a deliberate cancellation
  184. /// of listener IO as part of a normal stopListener call.
  185. ///
  186. /// If the result indicates a failed receive, stopListening() is invoked
  187. /// and the state is set to STOPPED_RECV_ERROR.
  188. ///
  189. /// This method specifically avoids throwing on an error as any such throw
  190. /// would surface at the io_service::run (or run variant) method invocation
  191. /// site. The upper layers are expected to monitor D2QueueMgr's state and
  192. /// act accordingly.
  193. ///
  194. /// @param result contains that receive outcome status.
  195. /// @param ncr is a pointer to the newly received NameChangeRequest if
  196. /// result is NameChangeListener::SUCCESS. It is indeterminate other
  197. /// wise.
  198. virtual void operator ()(const dhcp_ddns::NameChangeListener::Result result,
  199. dhcp_ddns::NameChangeRequestPtr& ncr);
  200. /// @brief Stops listening for requests.
  201. ///
  202. /// Invokes the listener's stopListening method which will cause it to
  203. /// cancel any pending IO and close its IO source. It the sets target
  204. /// stop state to the given value.
  205. ///
  206. /// If there is no IO pending, the manager state is immediately set to the
  207. /// target stop state, otherwise the manager state is set to STOPPING.
  208. ///
  209. /// @param target_stop_state is one of the three stopped state values.
  210. ///
  211. /// @throw D2QueueMgrError if stop_state is a valid stop state.
  212. void stopListening(const State target_stop_state = STOPPED);
  213. /// @brief Deletes the current listener
  214. ///
  215. /// This method will delete the current listener and returns the manager
  216. /// to the NOT_INITTED state. This is provided to support reconfiguring
  217. /// a new listener without losing queued requests.
  218. ///
  219. /// @throw D2QueMgrError if called when the manager state is RUNNING.
  220. void removeListener();
  221. /// @brief Returns the number of entries in the queue.
  222. size_t getQueueSize() const {
  223. return (ncr_queue_.size());
  224. };
  225. /// @brief Returns the maximum number of entries allowed in the queue.
  226. size_t getMaxQueueSize() const {
  227. return (max_queue_size_);
  228. }
  229. /// @brief Sets the maximum number of entries allowed in the queue.
  230. ///
  231. /// @param max_queue_size is the new maximum size of the queue.
  232. ///
  233. /// @throw D2QueueMgrError if the new value is less than one or if
  234. /// the new value is less than the number of entries currently in the
  235. /// queue.
  236. void setMaxQueueSize(const size_t max_queue_size);
  237. /// @brief Returns the current state.
  238. State getMgrState() const {
  239. return (mgr_state_);
  240. }
  241. /// @brief Returns the entry at the front of the queue.
  242. ///
  243. /// The entry returned is next in line to be processed, assuming a FIFO
  244. /// approach to task selection. Note, the entry is not removed from the
  245. /// queue.
  246. ///
  247. /// @return Pointer reference to the queue entry.
  248. ///
  249. /// @throw D2QueueMgrQueEmpty if there are no entries in the queue.
  250. const dhcp_ddns::NameChangeRequestPtr& peek() const;
  251. /// @brief Returns the entry at a given position in the queue.
  252. ///
  253. /// Note that the entry is not removed from the queue.
  254. /// @param index the index of the entry in the queue to fetch.
  255. /// Valid values are 0 (front of the queue) to (queue size - 1).
  256. ///
  257. /// @return Pointer reference to the queue entry.
  258. ///
  259. /// @throw D2QueueMgrInvalidIndex if the given index is beyond the
  260. /// end of the queue.
  261. const dhcp_ddns::NameChangeRequestPtr& peekAt(const size_t index) const;
  262. /// @brief Removes the entry at a given position in the queue.
  263. ///
  264. /// @param index the index of the entry in the queue to remove.
  265. /// Valid values are 0 (front of the queue) to (queue size - 1).
  266. ///
  267. /// @throw D2QueueMgrInvalidIndex if the given index is beyond the
  268. /// end of the queue.
  269. void dequeueAt(const size_t index);
  270. /// @brief Removes the entry at the front of the queue.
  271. ///
  272. /// @throw D2QueueMgrQueEmpty if there are no entries in the queue.
  273. void dequeue();
  274. /// @brief Adds a request to the end of the queue.
  275. ///
  276. /// @param ncr pointer to the NameChangeRequest to add to the queue.
  277. void enqueue(dhcp_ddns::NameChangeRequestPtr& ncr);
  278. /// @brief Removes all entries from the queue.
  279. void clearQueue();
  280. private:
  281. /// @brief Sets the manager state to the target stop state.
  282. ///
  283. /// Convenience method which sets the manager state to the target stop
  284. /// state and logs that the manager is stopped.
  285. void updateStopState();
  286. /// @brief IOService that our listener should use for IO management.
  287. asiolink::IOServicePtr io_service_;
  288. /// @brief Dictates the maximum number of entries allowed in the queue.
  289. size_t max_queue_size_;
  290. /// @brief Queue of received NameChangeRequests.
  291. RequestQueue ncr_queue_;
  292. /// @brief Listener instance from which requests are received.
  293. boost::shared_ptr<dhcp_ddns::NameChangeListener> listener_;
  294. /// @brief Current state of the manager.
  295. State mgr_state_;
  296. /// @brief Tracks the state the manager should be in once stopped.
  297. State target_stop_state_;
  298. };
  299. /// @brief Defines a pointer for manager instances.
  300. typedef boost::shared_ptr<D2QueueMgr> D2QueueMgrPtr;
  301. } // namespace isc::d2
  302. } // namespace isc
  303. #endif