d2_queue_mgr.h 14 KB

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