datasrc_clients_mgr.h 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. // Copyright (C) 2012 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 DATASRC_CLIENTS_MGR_H
  15. #define DATASRC_CLIENTS_MGR_H 1
  16. #include <util/threads/thread.h>
  17. #include <util/threads/sync.h>
  18. #include <log/logger_support.h>
  19. #include <log/log_dbglevels.h>
  20. #include <dns/rrclass.h>
  21. #include <cc/data.h>
  22. #include <datasrc/data_source.h>
  23. #include <datasrc/client_list.h>
  24. #include <auth/auth_log.h>
  25. #include <auth/datasrc_config.h>
  26. #include <boost/array.hpp>
  27. #include <boost/bind.hpp>
  28. #include <boost/shared_ptr.hpp>
  29. #include <boost/noncopyable.hpp>
  30. #include <exception>
  31. #include <cassert>
  32. #include <list>
  33. #include <utility>
  34. namespace isc {
  35. namespace auth {
  36. namespace datasrc_clientmgr_internal {
  37. // This namespace is essentially private for DataSrcClientsMgr(Base) and
  38. // DataSrcClientsBuilder(Base). This is exposed in the public header
  39. // only because these classes are templated (for testing purposes) and
  40. // class internal has to be defined here.
  41. /// \brief ID of commands from the DataSrcClientsMgr to DataSrcClientsBuilder.
  42. enum CommandID {
  43. NOOP, ///< Do nothing. Only useful for tests; no argument
  44. RECONFIGURE, ///< Reconfigure the datasource client lists,
  45. /// the argument to the command is the full new
  46. /// datasources configuration.
  47. LOADZONE, ///< Load a new version of zone into a memory,
  48. /// the argument to the command is a map containing 'class'
  49. /// and 'origin' elements, both should have been validated.
  50. SHUTDOWN, ///< Shutdown the builder; no argument
  51. NUM_COMMANDS
  52. };
  53. /// \brief The data type passed from DataSrcClientsMgr to
  54. /// DataSrcClientsBuilder.
  55. ///
  56. /// The first element of the pair is the command ID, and the second element
  57. /// is its argument. If the command doesn't take an argument it should be
  58. /// a null pointer.
  59. typedef std::pair<CommandID, data::ConstElementPtr> Command;
  60. } // namespace datasrc_clientmgr_internal
  61. /// \brief Frontend to the manager object for data source clients.
  62. ///
  63. /// This class provides interfaces for configuring and updating a set of
  64. /// data source clients "in the background". The user of this class can
  65. /// assume any operation on this class can be done effectively non-blocking,
  66. /// not suspending any delay-sensitive operations such as DNS query
  67. /// processing. The only exception is the time when this class object
  68. /// is destroyed (normally as a result of an implicit call to the destructor);
  69. /// in the current implementation it can take time depending on what is
  70. /// running "in the background" at the time of the call.
  71. ///
  72. /// Internally, an object of this class invokes a separate thread to perform
  73. /// time consuming operations such as loading large zone data into memory,
  74. /// but such details are completely hidden from the user of this class.
  75. ///
  76. /// This class is templated only so that we can test the class without
  77. /// involving actual threads or mutex. Normal applications will only
  78. /// need one specific specialization that has a typedef of
  79. /// \c DataSrcClientsMgr.
  80. template <typename ThreadType, typename BuilderType, typename MutexType,
  81. typename CondVarType>
  82. class DataSrcClientsMgrBase : boost::noncopyable {
  83. private:
  84. typedef std::map<dns::RRClass,
  85. boost::shared_ptr<datasrc::ConfigurableClientList> >
  86. ClientListsMap;
  87. public:
  88. /// \brief Thread-safe accessor to the data source client lists.
  89. ///
  90. /// This class provides a simple wrapper for searching the client lists
  91. /// stored in the DataSrcClientsMgr in a thread-safe manner.
  92. /// It ensures the result of \c getClientList() can be used without
  93. /// causing a race condition with other threads that can possibly use
  94. /// the same manager throughout the lifetime of the holder object.
  95. ///
  96. /// This also means the holder object is expected to have a short lifetime.
  97. /// The application shouldn't try to keep it unnecessarily long.
  98. /// It's normally expected to create the holder object on the stack
  99. /// of a small scope and automatically let it be destroyed at the end
  100. /// of the scope.
  101. class Holder {
  102. public:
  103. Holder(DataSrcClientsMgrBase& mgr) :
  104. mgr_(mgr), locker_(mgr_.map_mutex_)
  105. {}
  106. /// \brief Find a data source client list of a specified RR class.
  107. ///
  108. /// It returns a pointer to the list stored in the manager if found,
  109. /// otherwise it returns NULL. The manager keeps the ownership of
  110. /// the pointed object. Also, it's not safe to get access to the
  111. /// object beyond the scope of the holder object.
  112. ///
  113. /// \note Since the ownership isn't transferred the return value
  114. /// could be a bare pointer (and it's probably better in several
  115. /// points). Unfortunately, some unit tests currently don't work
  116. /// unless this method effectively shares the ownership with the
  117. /// tests. That's the only reason why we return a shared pointer
  118. /// for now. We should eventually fix it and change the return value
  119. /// type (see Trac ticket #2395). Other applications must not
  120. /// assume the ownership is actually shared.
  121. boost::shared_ptr<datasrc::ConfigurableClientList> findClientList(
  122. const dns::RRClass& rrclass)
  123. {
  124. const ClientListsMap::const_iterator
  125. it = mgr_.clients_map_->find(rrclass);
  126. if (it == mgr_.clients_map_->end()) {
  127. return (boost::shared_ptr<datasrc::ConfigurableClientList>());
  128. } else {
  129. return (it->second);
  130. }
  131. }
  132. private:
  133. DataSrcClientsMgrBase& mgr_;
  134. typename MutexType::Locker locker_;
  135. };
  136. /// \brief Constructor.
  137. ///
  138. /// It internally invokes a separate thread and waits for further
  139. /// operations from the user application.
  140. ///
  141. /// This method is basically exception free except in case of really
  142. /// rare system-level errors. When that happens the only reasonable
  143. /// action that the application can take would be to terminate the program
  144. /// in practice.
  145. ///
  146. /// \throw std::bad_alloc internal memory allocation failure.
  147. /// \throw isc::Unexpected general unexpected system errors.
  148. DataSrcClientsMgrBase() :
  149. clients_map_(new ClientListsMap),
  150. builder_(&command_queue_, &cond_, &queue_mutex_, &clients_map_,
  151. &map_mutex_),
  152. builder_thread_(boost::bind(&BuilderType::run, &builder_))
  153. {}
  154. /// \brief The destructor.
  155. ///
  156. /// It tells the internal thread to stop and waits for it completion.
  157. /// In the current implementation, it can block for some unpredictably
  158. /// long period depending on what the thread is doing at that time
  159. /// (in future we may want to implement a rapid way of killing the thread
  160. /// and/or provide a separate interface for waiting so that the application
  161. /// can choose the timing).
  162. ///
  163. /// The waiting operation can result in an exception, but this method
  164. /// catches any of them so this method itself is exception free.
  165. ~DataSrcClientsMgrBase() {
  166. // We share class member variables with the builder, which will be
  167. // invalidated after the call to the destructor, so we need to make
  168. // sure the builder thread is terminated. Depending on the timing
  169. // this could take a long time; if we don't want that to happen in
  170. // this context, we may want to introduce a separate 'shutdown()'
  171. // method.
  172. // Also, since we don't want to propagate exceptions from a destructor,
  173. // we catch any possible ones. In fact the only really expected one
  174. // is Thread::UncaughtException when the builder thread died due to
  175. // an exception. We specifically log it and just ignore others.
  176. try {
  177. sendCommand(datasrc_clientmgr_internal::SHUTDOWN,
  178. data::ConstElementPtr());
  179. builder_thread_.wait();
  180. } catch (const util::thread::Thread::UncaughtException& ex) {
  181. // technically, logging this could throw, which will be propagated.
  182. // But such an exception would be a fatal one anyway, so we
  183. // simply let it go through.
  184. LOG_ERROR(auth_logger, AUTH_DATASRC_CLIENTS_SHUTDOWN_ERROR).
  185. arg(ex.what());
  186. } catch (...) {
  187. LOG_ERROR(auth_logger,
  188. AUTH_DATASRC_CLIENTS_SHUTDOWN_UNEXPECTED_ERROR);
  189. }
  190. cleanup(); // see below
  191. }
  192. /// \brief Handle new full configuration for data source clients.
  193. ///
  194. /// This method simply passes the new configuration to the builder
  195. /// and immediately returns. This method is basically exception free
  196. /// as long as the caller passes a non NULL value for \c config_arg;
  197. /// it doesn't validate the argument further.
  198. ///
  199. /// \brief isc::InvalidParameter config_arg is NULL.
  200. /// \brief std::bad_alloc
  201. ///
  202. /// \param config_arg The new data source configuration. Must not be NULL.
  203. void reconfigure(data::ConstElementPtr config_arg) {
  204. if (!config_arg) {
  205. isc_throw(InvalidParameter, "Invalid null config argument");
  206. }
  207. sendCommand(datasrc_clientmgr_internal::RECONFIGURE, config_arg);
  208. reconfigureHook(); // for test's customization
  209. }
  210. /// \brief Set the underlying data source client lists to new lists.
  211. ///
  212. /// This is provided only for some existing tests until we support a
  213. /// cleaner way to use faked data source clients. Non test code or
  214. /// newer tests must not use this.
  215. void setDataSrcClientLists(datasrc::ClientListMapPtr new_lists) {
  216. typename MutexType::Locker locker(map_mutex_);
  217. clients_map_ = new_lists;
  218. }
  219. private:
  220. // This is expected to be called at the end of the destructor. It
  221. // actually does nothing, but provides a customization point for
  222. // specialized class for tests so that the tests can inspect the last
  223. // state of the class.
  224. void cleanup() {}
  225. // same as cleanup(), for reconfigure().
  226. void reconfigureHook() {}
  227. void sendCommand(datasrc_clientmgr_internal::CommandID command,
  228. data::ConstElementPtr arg)
  229. {
  230. // The lock will be held until the end of this method. Only
  231. // push_back has to be protected, but we can avoid having an extra
  232. // block this way.
  233. typename MutexType::Locker locker(queue_mutex_);
  234. command_queue_.push_back(
  235. datasrc_clientmgr_internal::Command(command, arg));
  236. cond_.signal();
  237. }
  238. //
  239. // The following are shared with the builder.
  240. //
  241. // The list is used as a one-way queue: back-in, front-out
  242. std::list<datasrc_clientmgr_internal::Command> command_queue_;
  243. CondVarType cond_; // condition variable for queue operations
  244. MutexType queue_mutex_; // mutex to protect the queue
  245. datasrc::ClientListMapPtr clients_map_;
  246. // map of actual data source client objects
  247. MutexType map_mutex_; // mutex to protect the clients map
  248. BuilderType builder_;
  249. ThreadType builder_thread_; // for safety this should be placed last
  250. };
  251. namespace datasrc_clientmgr_internal {
  252. /// \brief A class that maintains a set of data source clients.
  253. ///
  254. /// An object of this class is supposed to run on a dedicated thread, whose
  255. /// main function is a call to its \c run() method. It runs in a loop
  256. /// waiting for commands from the manager and handles each command (including
  257. /// reloading a new version of zone data into memory or fully reconfiguration
  258. /// of specific set of data source clients). When it receives a SHUTDOWN
  259. /// command, it exits from the loop, which will terminate the thread.
  260. ///
  261. /// While this class is defined in a publicly visible namespace, it's
  262. /// essentially private to \c DataSrcClientsMgr. Except for tests,
  263. /// applications should not directly access this class.
  264. ///
  265. /// This class is templated so that we can test it without involving actual
  266. /// threads or locks.
  267. template <typename MutexType, typename CondVarType>
  268. class DataSrcClientsBuilderBase : boost::noncopyable {
  269. private:
  270. typedef std::map<dns::RRClass,
  271. boost::shared_ptr<datasrc::ConfigurableClientList> >
  272. ClientListsMap;
  273. public:
  274. /// \brief Internal errors in handling commands.
  275. ///
  276. /// This exception is expected to be caught within the
  277. /// \c DataSrcClientsBuilder implementation, but is defined as public
  278. /// so tests can be checked it.
  279. class InternalCommandError : public isc::Exception {
  280. public:
  281. InternalCommandError(const char* file, size_t line, const char* what) :
  282. isc::Exception(file, line, what) {}
  283. };
  284. /// \brief Constructor.
  285. ///
  286. /// It simply sets up a local copy of shared data with the manager.
  287. ///
  288. /// Note: this will take actual set (map) of data source clients and
  289. /// a mutex object for it in #2210 or #2212.
  290. ///
  291. /// \throw None
  292. DataSrcClientsBuilderBase(std::list<Command>* command_queue,
  293. CondVarType* cond, MutexType* queue_mutex,
  294. datasrc::ClientListMapPtr* clients_map,
  295. MutexType* map_mutex
  296. ) :
  297. command_queue_(command_queue), cond_(cond), queue_mutex_(queue_mutex),
  298. clients_map_(clients_map), map_mutex_(map_mutex)
  299. {}
  300. /// \brief The main loop.
  301. void run();
  302. /// \brief Handle one command from the manager.
  303. ///
  304. /// This is a dedicated subroutine of run() and is essentially private,
  305. /// but is defined as a separate public method so we can test each
  306. /// command test individually. In any case, this class itself is
  307. /// generally considered private.
  308. ///
  309. /// \return true if the builder should keep running; false otherwise.
  310. bool handleCommand(const Command& command);
  311. private:
  312. // NOOP command handler. We use this so tests can override it; the default
  313. // implementation really does nothing.
  314. void doNoop() {}
  315. void doReconfigure(const data::ConstElementPtr& config) {
  316. if (config) {
  317. LOG_INFO(auth_logger,
  318. AUTH_DATASRC_CLIENTS_BUILDER_RECONFIGURE_STARTED);
  319. try {
  320. // Define new_clients_map outside of the block that
  321. // has the lock scope; this way, after the swap,
  322. // the lock is guaranteed to be released before
  323. // the old data is destroyed, minimizing the lock
  324. // duration.
  325. datasrc::ClientListMapPtr new_clients_map =
  326. configureDataSource(config);
  327. {
  328. typename MutexType::Locker locker(*map_mutex_);
  329. new_clients_map.swap(*clients_map_);
  330. } // lock is released by leaving scope
  331. LOG_INFO(auth_logger,
  332. AUTH_DATASRC_CLIENTS_BUILDER_RECONFIGURE_SUCCESS);
  333. } catch (const datasrc::ConfigurableClientList::ConfigurationError&
  334. config_error) {
  335. LOG_ERROR(auth_logger,
  336. AUTH_DATASRC_CLIENTS_BUILDER_RECONFIGURE_CONFIG_ERROR).
  337. arg(config_error.what());
  338. } catch (const datasrc::DataSourceError& ds_error) {
  339. LOG_ERROR(auth_logger,
  340. AUTH_DATASRC_CLIENTS_BUILDER_RECONFIGURE_DATASRC_ERROR).
  341. arg(ds_error.what());
  342. } catch (const isc::Exception& isc_error) {
  343. LOG_ERROR(auth_logger,
  344. AUTH_DATASRC_CLIENTS_BUILDER_RECONFIGURE_ERROR).
  345. arg(isc_error.what());
  346. }
  347. // other exceptions are propagated, see
  348. // http://bind10.isc.org/ticket/2210#comment:13
  349. // old clients_map_ data is released by leaving scope
  350. }
  351. }
  352. void doLoadZone(const isc::data::ConstElementPtr& arg);
  353. // The following are shared with the manager
  354. std::list<Command>* command_queue_;
  355. CondVarType* cond_;
  356. MutexType* queue_mutex_;
  357. datasrc::ClientListMapPtr* clients_map_;
  358. MutexType* map_mutex_;
  359. };
  360. // Shortcut typedef for normal use
  361. typedef DataSrcClientsBuilderBase<util::thread::Mutex, util::thread::CondVar>
  362. DataSrcClientsBuilder;
  363. template <typename MutexType, typename CondVarType>
  364. void
  365. DataSrcClientsBuilderBase<MutexType, CondVarType>::run() {
  366. LOG_INFO(auth_logger, AUTH_DATASRC_CLIENTS_BUILDER_STARTED);
  367. try {
  368. bool keep_running = true;
  369. while (keep_running) {
  370. std::list<Command> current_commands;
  371. {
  372. // Move all new commands to local queue under the protection of
  373. // queue_mutex_.
  374. typename MutexType::Locker locker(*queue_mutex_);
  375. while (command_queue_->empty()) {
  376. cond_->wait(*queue_mutex_);
  377. }
  378. current_commands.swap(*command_queue_);
  379. } // the lock is released here.
  380. while (keep_running && !current_commands.empty()) {
  381. keep_running = handleCommand(current_commands.front());
  382. current_commands.pop_front();
  383. }
  384. }
  385. LOG_INFO(auth_logger, AUTH_DATASRC_CLIENTS_BUILDER_STOPPED);
  386. } catch (const std::exception& ex) {
  387. // We explicitly catch exceptions so we can log it as soon as possible.
  388. LOG_FATAL(auth_logger, AUTH_DATASRC_CLIENTS_BUILDER_FAILED).
  389. arg(ex.what());
  390. std::terminate();
  391. } catch (...) {
  392. LOG_FATAL(auth_logger, AUTH_DATASRC_CLIENTS_BUILDER_FAILED_UNEXPECTED);
  393. std::terminate();
  394. }
  395. }
  396. template <typename MutexType, typename CondVarType>
  397. bool
  398. DataSrcClientsBuilderBase<MutexType, CondVarType>::handleCommand(
  399. const Command& command)
  400. {
  401. const CommandID cid = command.first;
  402. if (cid >= NUM_COMMANDS) {
  403. // This shouldn't happen except for a bug within this file.
  404. isc_throw(Unexpected, "internal bug: invalid command, ID: " << cid);
  405. }
  406. const boost::array<const char*, NUM_COMMANDS> command_desc = {
  407. {"NOOP", "RECONFIGURE", "LOADZONE", "SHUTDOWN"}
  408. };
  409. LOG_DEBUG(auth_logger, DBGLVL_TRACE_BASIC,
  410. AUTH_DATASRC_CLIENTS_BUILDER_COMMAND).arg(command_desc.at(cid));
  411. switch (command.first) {
  412. case RECONFIGURE:
  413. doReconfigure(command.second);
  414. break;
  415. case LOADZONE:
  416. doLoadZone(command.second);
  417. break;
  418. case SHUTDOWN:
  419. return (false);
  420. case NOOP:
  421. doNoop();
  422. break;
  423. case NUM_COMMANDS:
  424. assert(false); // we rejected this case above
  425. }
  426. return (true);
  427. }
  428. template <typename MutexType, typename CondVarType>
  429. void
  430. DataSrcClientsBuilderBase<MutexType, CondVarType>::doLoadZone(
  431. const isc::data::ConstElementPtr& arg)
  432. {
  433. // TODO: test bogus class and name
  434. const dns::RRClass rrclass(arg->get("class")->stringValue());
  435. const dns::Name origin(arg->get("origin")->stringValue());
  436. ClientListsMap::iterator found = (*clients_map_)->find(rrclass);
  437. if (found == (*clients_map_)->end()) {
  438. isc_throw(InternalCommandError, "failed to load a zone " << origin <<
  439. "/" << rrclass << ": not configured for the class");
  440. return;
  441. }
  442. boost::shared_ptr<datasrc::ConfigurableClientList> client_list =
  443. found->second;
  444. assert(client_list);
  445. datasrc::ConfigurableClientList::ReloadResult result;
  446. {
  447. typename MutexType::Locker locker(*map_mutex_);
  448. result = client_list->reload(origin);
  449. }
  450. switch (result) {
  451. case datasrc::ConfigurableClientList::ZONE_RELOADED:
  452. // Everything worked fine.
  453. //LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_LOAD_ZONE)
  454. // .arg(zone_class).arg(origin);
  455. break;
  456. case datasrc::ConfigurableClientList::ZONE_NOT_FOUND:
  457. isc_throw(InternalCommandError, "failed to load zone " << origin
  458. << "/" << rrclass << ": not found in any configured "
  459. "data source.");
  460. default:
  461. assert(false);
  462. }
  463. }
  464. } // namespace datasrc_clientmgr_internal
  465. /// \brief Shortcut type for normal data source clients manager.
  466. ///
  467. /// In fact, for non test applications this is the only type of this kind
  468. /// to be considered.
  469. typedef DataSrcClientsMgrBase<
  470. util::thread::Thread,
  471. datasrc_clientmgr_internal::DataSrcClientsBuilder,
  472. util::thread::Mutex, util::thread::CondVar> DataSrcClientsMgr;
  473. } // namespace auth
  474. } // namespace isc
  475. #endif // DATASRC_CLIENTS_MGR_H
  476. // Local Variables:
  477. // mode: c++
  478. // End: