datasrc_clients_mgr.h 27 KB

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