nc_trans.h 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  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 NC_TRANS_H
  15. #define NC_TRANS_H
  16. /// @file nc_trans.h This file defines the class NameChangeTransaction.
  17. #include <asiolink/io_service.h>
  18. #include <exceptions/exceptions.h>
  19. #include <d2/d2_config.h>
  20. #include <d2/dns_client.h>
  21. #include <dhcp_ddns/ncr_msg.h>
  22. #include <boost/shared_ptr.hpp>
  23. #include <map>
  24. namespace isc {
  25. namespace d2 {
  26. /// @brief Thrown if the update manager encounters a general error.
  27. class NameChangeTransactionError : public isc::Exception {
  28. public:
  29. NameChangeTransactionError(const char* file, size_t line,
  30. const char* what) :
  31. isc::Exception(file, line, what) { };
  32. };
  33. /// @brief Defines the type used as the unique key for transactions.
  34. typedef isc::dhcp_ddns::D2Dhcid TransactionKey;
  35. /// @brief Defines a function pointer for the handler method for a state.
  36. typedef boost::function<void()> StateHandler;
  37. /// @brief Defines a map of states to their handler methods.
  38. typedef std::map<unsigned int, StateHandler> StateHandlerMap;
  39. /// @brief Embodies the "life-cycle" required to carry out a DDNS update.
  40. ///
  41. /// NameChangeTransaction is the base class that provides the common state
  42. /// model mechanics and services performing the DNS updates needed to carry out
  43. /// a DHCP_DDNS request as described by a NameChangeRequest.
  44. ///
  45. /// Upon construction, each transaction has all of the information and
  46. /// resources required to carry out its assigned request, including the list(s)
  47. /// of DNS server(s) needed. It is responsible for knowing what conversations
  48. /// it must have with which servers and in the order necessary to fulfill the
  49. /// request. Upon fulfillment of the request, the transaction's work is complete
  50. /// it is destroyed.
  51. ///
  52. /// Fulfillment of the request is carried out through the performance of the
  53. /// transaction's state model. Using a state driven implementation accounts
  54. /// for the conditional processing flow necessary to meet the DDNS RFCs as well
  55. /// as the asynchronous nature of IO with DNS servers.
  56. ///
  57. /// Derivations of the class are responsible for defining the state model and
  58. /// conversations necessary to carry out the specific of request.
  59. ///
  60. /// Conversations with DNS servers are done through the use of the DNSClient
  61. /// class. The DNSClient provides a IOService-based means a service which
  62. /// performs a single, packet exchange with a given DNS server. It sends a
  63. /// single update to the server and returns the response, asynchronously,
  64. /// through a callback. At each point in a transaction's state model, where
  65. /// an update is to be sent, the model "suspends" until notified by the
  66. /// DNSClient via the callback.
  67. ///
  68. /// The state model implementation used is a very basic approach. States
  69. /// and events are simple integer constants. Each state must have a state
  70. /// handler. State handlers are void methods which accept an event as their
  71. /// only parameter. Each transaction instance contains a map of states to
  72. /// to bound method pointers to their respective state handlers.
  73. ///
  74. /// When invoked, the handler determines what it should do based upon the event,
  75. /// including what the next state and event should be. In other words the state
  76. /// transition knowledge is distributed among the state handlers rather than
  77. /// encapsulated in some form of state transition table. Events set from within
  78. /// the state handlers are "internally" triggered events. Events set from
  79. /// outside the state model, such as through the DNSClient completion callback
  80. /// are "externally" triggered.
  81. ///
  82. /// Executing the model consists of iteratively invoking the state handler
  83. /// indicated by the current state and passing it the current event. As the
  84. /// handlers update the state and event, the machine is traversed. The loop
  85. /// "stops" whenever the model cannot continue without an externally triggered
  86. /// event or when it has reached its final state. In the case of the former,
  87. /// the loop is re-entered upon arrival of the external event.
  88. ///
  89. /// This loop is implemented in the runStateModel method. This method accepts
  90. /// an event as argument. This event is treated as the "next event" to process
  91. /// and is fed to the current state's handler. The runStateModel does not exit
  92. /// until a handler sets the next event to a special value, NOP_EVT,
  93. /// indicating that either it is now waiting for IO to complete of the state
  94. /// model has reached its conclusion.
  95. ///
  96. /// Re-entering the "loop" when a DNS update completes is done by a call to
  97. /// runStateModel() from within the DNSClient callback, with an event value
  98. /// of IO_COMPLETED_EVT. As above, runStateModel() will loop until either the
  99. /// next IO is issued or the state model has reached its conclusion.
  100. ///
  101. /// This class defines a set of events and states that are a common to all
  102. /// transactions. Each derivation may add define additional states and events
  103. /// as needed, but it must support the common set. NameChangeTransaction
  104. /// does not supply any state handlers. These are the sole responsibility of
  105. /// derivations.
  106. class NameChangeTransaction : public DNSClient::Callback {
  107. public:
  108. //@{ States common to all transactions.
  109. /// @brief State a transaction is in immediately after construction.
  110. static const int NEW_ST = 0;
  111. /// @brief State from which a transaction is started.
  112. static const int READY_ST = 1;
  113. /// @brief State in which forward DNS server selection is done.
  114. static const int SELECTING_FWD_SERVER_ST = 2;
  115. /// @brief State in which reverse DNS server selection is done.
  116. static const int SELECTING_REV_SERVER_ST = 3;
  117. /// @brief Final state, all work has been performed.
  118. static const int DONE_ST = 4;
  119. /// @define Value at which custom states in a derived class should begin.
  120. static const int DERIVED_STATES = 100;
  121. //@}
  122. //@{ Events common to all transactions.
  123. /// @brief Signifies that no event has occurred.
  124. /// This is event used to interrupt the event loop to allow waiting for
  125. /// an IO event or when there is no more work to be done.
  126. static const int NOP_EVT = 0;
  127. /// @brief Event used to start the transaction.
  128. static const int START_TRANSACTION_EVT = 1;
  129. /// @brief Issued when a server needs to be selected.
  130. static const int SELECT_SERVER_EVT = 2;
  131. /// @brief Issued when a server has been selected.
  132. static const int SERVER_SELECTED_EVT = 3;
  133. /// @brief Issued when an update fails due to an IO error.
  134. static const int SERVER_IO_ERROR_EVT = 4;
  135. /// @brief Issued when there are no more servers from which to select.
  136. /// This occurs when none of the servers in the list can be reached to
  137. /// perform the update.
  138. static const int NO_MORE_SERVERS_EVT = 5;
  139. /// @brief Issued when a DNS update packet exchange has completed.
  140. /// This occurs whenever the DNSClient callback is invoked whether the
  141. /// exchange was successful or not.
  142. static const int IO_COMPLETED_EVT = 6;
  143. /// @brief Issued when the attempted update successfully completed.
  144. /// This occurs when an DNS update packet was successfully processed
  145. /// by the server.
  146. static const int UPDATE_OK_EVT = 7;
  147. /// @brief Issued when the attempted update fails to complete.
  148. /// This occurs when an DNS update packet fails to process. The nature of
  149. /// the failure is given by the DNSClient return status and the response
  150. /// packet (if one was received).
  151. static const int UPDATE_FAILED_EVT = 8;
  152. /// @brief Issued when the transaction should be cancelled.
  153. /// @todo - still on the fence about this one.
  154. static const int CANCEL_TRANSACTION_EVT = 9;
  155. /// @brief Issued when the state model has no more work left to do.
  156. static const int ALL_DONE_EVT = 10;
  157. /// @define Value at which custom events in a derived class should begin.
  158. static const int DERIVED_EVENTS = 100;
  159. //@}
  160. /// @brief Constructor
  161. ///
  162. /// Instantiates a transaction that is ready to be started.
  163. ///
  164. /// @param io_service IO service to be used for IO processing
  165. /// @param ncr is the NameChangeRequest to fulfill
  166. /// @param forward_domain is the domain to use for forward DNS updates
  167. /// @param reverse_domain is the domain to use for reverse DNS updates
  168. ///
  169. /// @throw NameChangeTransaction error if given an null request,
  170. /// if forward change is enabled but forward domain is null, if
  171. /// reverse change is enabled but reverse domain is null.
  172. NameChangeTransaction(isc::asiolink::IOService& io_service,
  173. dhcp_ddns::NameChangeRequestPtr& ncr,
  174. DdnsDomainPtr forward_domain,
  175. DdnsDomainPtr reverse_domain);
  176. virtual ~NameChangeTransaction();
  177. /// @brief Begins execution of the transaction.
  178. ///
  179. /// This method invokes initHandlersMap() to initialize the map of state
  180. /// handlers. It then starts the transaction's state model by setting the
  181. /// current state to READY_ST and invoking runStateModel() with an event
  182. /// parameter of START_TRANSACTION_EVT.
  183. void startTransaction();
  184. /// @todo - Not sure about this yet.
  185. void cancelTransaction();
  186. /// @brief Serves as the DNSClient IO completion event handler.
  187. ///
  188. /// This is the implementation of the method inherited by our derivation
  189. /// from DNSClient::Callback. When the DNSClient completes an update it
  190. /// invokes this method as the completion handler. This method stores
  191. /// the given status and invokes runStateModel() with an event value of
  192. /// IO_COMPLETED_EVT.
  193. ///
  194. /// @param status is the outcome of the DNS update packet exchange.
  195. /// This method is exception safe.
  196. virtual void operator()(DNSClient::Status status);
  197. protected:
  198. /// @brief Populates the map of state handlers.
  199. ///
  200. /// This method is used by derivations to construct a map of states to
  201. /// their appropriate state handlers (bound method pointers). It is
  202. /// invoked at the beginning of startTransaction().
  203. ///
  204. /// Implementations should use the addToMap() method add entries to
  205. /// the map.
  206. /// @todo This method should be pure virtual but until there are
  207. /// derivations for the update manager to use we will provide an
  208. /// temporary empty, implementation. If we make it pure virtual now
  209. /// D2UpdateManager will not compile.
  210. virtual void initStateHandlerMap() {};
  211. /// @brief Adds an entry to the state handler map.
  212. ///
  213. /// This method attempts to add an entry to the handler map which maps
  214. /// the given handler to the given state. The state handler must be
  215. /// a bound member pointer to a handler method of the transaction instance.
  216. /// The following code snippet shows an example derivation and call to
  217. /// addToMap() within its initStateHandlerMap() method.
  218. ///
  219. /// @code
  220. /// class ExampleTrans : public NameChangeTransaction {
  221. /// public:
  222. /// :
  223. /// void readyHandler() {
  224. /// }
  225. ///
  226. /// void initStateHandlerMap() {
  227. /// addToMap(READY_ST,
  228. /// boost::bind(&ExampleTrans::readyHandler, this));
  229. /// :
  230. ///
  231. /// @endcode
  232. ///
  233. /// @param state the value of the state to which to map
  234. /// @param handler the bound method pointer to the handler for the state
  235. ///
  236. /// @throw NameChangeTransactionError if the map already contains an entry
  237. /// for the given state.
  238. void addToMap(unsigned int idx, StateHandler handler);
  239. /// @brief Processes events through the state model
  240. ///
  241. /// This method implements the state model "execution loop". It uses
  242. /// the given event as the next event to process and begins looping by
  243. /// passing it the state handler for the current state. As described
  244. /// above, the invoked state handler determines the current state and the
  245. /// next event required to implement the business logic. The method
  246. /// continues to loop until next event is set to NOP_EVT, at which point
  247. /// the method exits.
  248. ///
  249. /// Any exception thrown during the loop is caught, logged, and the
  250. /// transaction is immediately set to failed status. The state model is
  251. /// expected to account for any possible errors so any that escape are
  252. /// treated as unrecoverable in terms of the current transaction.
  253. ///
  254. /// @param event is the next event to process
  255. ///
  256. /// This is guaranteed not to throw.
  257. void runStateModel(unsigned int event);
  258. /// @brief Return the state handler for a given state.
  259. ///
  260. /// This method looks up the state handler for the given state from within
  261. /// the state handler map.
  262. ///
  263. /// @param state is the state constant of the desired handler.
  264. ///
  265. /// @return A StateHandler (bound method pointer) for the method that
  266. /// handles the given state for this transaction.
  267. ///
  268. /// @throw NameChangeTransactionError
  269. StateHandler getStateHandler(unsigned int state);
  270. /// @brief Sets the current state to the given state value.
  271. ///
  272. /// This updates the transaction's notion of the current state and is the
  273. /// state whose handler will be executed on the next iteration of the run
  274. /// loop.
  275. ///
  276. /// @param state the new value to assign to the current state.
  277. void setState(unsigned int state);
  278. /// @brief Sets the next event to the given event value.
  279. ///
  280. /// This updates the transaction's notion of the next event and is the
  281. /// event that will be passed into the current state's handler on the next
  282. /// iteration of the run loop.
  283. ///
  284. /// @param state the new value to assign to the current state.
  285. void setNextEvent(unsigned int event);
  286. /// @brief Sets the update status to the given status value.
  287. ///
  288. /// @param status is the new value for the update status.
  289. void setDnsUpdateStatus(const DNSClient::Status& status);
  290. /// @brief Sets the update response packet to the given packet.
  291. ///
  292. /// @param response is the new response packet to assign.
  293. void setDnsUpdateResponse(D2UpdateMessagePtr& response);
  294. /// @brief Sets the forward change completion flag to the given value.
  295. ///
  296. /// @param value is the new value to assign to the flag.
  297. void setForwardChangeCompleted(const bool value);
  298. /// @brief Sets the reverse change completion flag to the given value.
  299. ///
  300. /// @param value is the new value to assign to the flag.
  301. void setReverseChangeCompleted(const bool value);
  302. public:
  303. /// @brief Fetches the NameChangeRequest for this transaction.
  304. ///
  305. /// @return A const pointer reference to the NameChangeRequest.
  306. const dhcp_ddns::NameChangeRequestPtr& getNcr() const;
  307. /// @brief Fetches the unique key that identifies this transaction.
  308. ///
  309. /// Transactions are uniquely identified by a TransactionKey. Currently
  310. /// this is wrapper around a D2Dhcid.
  311. ///
  312. /// @return A const reference to the TransactionKey.
  313. const TransactionKey& getTransactionKey() const;
  314. /// @brief Fetches the NameChangeRequest status of the transaction.
  315. ///
  316. /// This is the current status of the NameChangeRequest, not to
  317. /// be confused with the state of the transaction. Once the transaction
  318. /// is reached it's conclusion, the request will end up with a final
  319. /// status.
  320. ///
  321. /// @return A dhcp_ddns::NameChangeStatus representing the current
  322. /// status of the transaction.
  323. dhcp_ddns::NameChangeStatus getNcrStatus() const;
  324. /// @brief Sets the status of the transaction's NameChangeRequest
  325. ///
  326. /// @param status is the new value to assign to the NCR status.
  327. void setNcrStatus(const dhcp_ddns::NameChangeStatus& status);
  328. /// @brief Fetches the transaction's current state.
  329. ///
  330. /// This returns the transaction's notion of the current state. It is the
  331. /// state whose handler will be executed on the next iteration of the run
  332. /// loop.
  333. ///
  334. /// @return An unsigned int representing the current state.
  335. unsigned int getState() const;
  336. /// @brief Fetches the transaction's previous state.
  337. ///
  338. /// @return An unsigned int representing the previous state.
  339. unsigned int getPrevState() const;
  340. /// @brief Fetches the transaction's last event.
  341. ///
  342. /// @return An unsigned int representing the last event.
  343. unsigned int getLastEvent() const;
  344. /// @brief Fetches the transaction's next event.
  345. ///
  346. /// This returns the transaction's notion of the next event. It is the
  347. /// event that will be passed into the current state's handler on the next
  348. /// iteration of the run loop.
  349. ///
  350. /// @return An unsigned int representing the next event.
  351. unsigned int getNextEvent() const;
  352. /// @brief Fetches the most recent DNS update status.
  353. ///
  354. /// @return A DNSClient::Status indicating the result of the most recent
  355. /// DNS update to complete.
  356. DNSClient::Status getDnsUpdateStatus() const;
  357. /// @brief Fetches the most recent DNS update response packet.
  358. ///
  359. /// @return A const pointer reference to the D2UpdateMessage most recently
  360. /// received.
  361. const D2UpdateMessagePtr& getDnsUpdateResponse() const;
  362. /// @brief Returns whether the forward change has completed or not.
  363. ///
  364. /// The value returned is only meaningful if the NameChangeRequest calls
  365. /// for a forward change to be done. The value returned indicates if
  366. /// forward change has been completed successfully.
  367. ///
  368. /// @return True if the forward change has been completed, false otherwise.
  369. bool getForwardChangeCompleted() const;
  370. /// @brief Returns whether the reverse change has completed or not.
  371. ///
  372. /// The value returned is only meaningful if the NameChangeRequest calls
  373. /// for a reverse change to be done. The value returned indicates if
  374. /// reverse change has been completed successfully.
  375. ///
  376. /// @return True if the reverse change has been completed, false otherwise.
  377. bool getReverseChangeCompleted() const;
  378. private:
  379. /// @brief Contains a map of states to their state handlers.
  380. StateHandlerMap state_handlers_;
  381. /// @brief The IOService which should be used to for IO processing.
  382. isc::asiolink::IOService& io_service_;
  383. /// @brief The NameChangeRequest that the transaction is to fulfill.
  384. dhcp_ddns::NameChangeRequestPtr ncr_;
  385. /// @brief The forward domain that matches the request.
  386. ///
  387. /// The forward "domain" is DdnsDomain which contains all of the information
  388. /// necessary, including the list of DNS servers to be used for a forward
  389. /// change.
  390. DdnsDomainPtr forward_domain_;
  391. /// @brief The reverse domain that matches the request.
  392. ///
  393. /// The reverse "domain" is DdnsDomain which contains all of the information
  394. /// necessary, including the list of DNS servers to be used for a reverse
  395. /// change.
  396. DdnsDomainPtr reverse_domain_;
  397. /// @brief The DNSClient instance that will carry out DNS packet exchanges.
  398. DNSClientPtr dns_client_;
  399. /// @brief The current state within the transaction's state model.
  400. unsigned int state_;
  401. /// @brief The previous state within the transaction's state model.
  402. unsigned int prev_state_;
  403. /// @brief The event last processed by the transaction.
  404. unsigned int last_event_;
  405. /// @brief The event the transaction should process next.
  406. unsigned int next_event_;
  407. /// @brief The outcome of the most recently completed DNS packet exchange.
  408. DNSClient::Status dns_update_status_;
  409. /// @brief The DNS update response packet most recently received.
  410. D2UpdateMessagePtr dns_update_response_;
  411. /// @brief Indicator for whether or not the forward change completed ok.
  412. bool forward_change_completed_;
  413. /// @brief Indicator for whether or not the reverse change completed ok.
  414. bool reverse_change_completed_;
  415. };
  416. /// @brief Defines a pointer to a NameChangeTransaction.
  417. typedef boost::shared_ptr<NameChangeTransaction> NameChangeTransactionPtr;
  418. } // namespace isc::d2
  419. } // namespace isc
  420. #endif