client_list.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. // Copyright (C) 2012-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. #include <datasrc/client_list.h>
  15. #include <datasrc/exceptions.h>
  16. #include <datasrc/client.h>
  17. #include <datasrc/factory.h>
  18. #include <datasrc/cache_config.h>
  19. #include <datasrc/memory/memory_client.h>
  20. #include <datasrc/memory/zone_table_segment.h>
  21. #include <datasrc/memory/zone_writer.h>
  22. #include <datasrc/memory/zone_data_loader.h>
  23. #include <datasrc/memory/zone_data_updater.h>
  24. #include <datasrc/logger.h>
  25. #include <datasrc/zone_table_accessor_cache.h>
  26. #include <dns/masterload.h>
  27. #include <util/memory_segment_local.h>
  28. #include <memory>
  29. #include <set>
  30. #include <boost/foreach.hpp>
  31. #include <boost/bind.hpp>
  32. #include <boost/scoped_ptr.hpp>
  33. using namespace isc::data;
  34. using namespace isc::dns;
  35. using namespace std;
  36. using isc::util::MemorySegment;
  37. using boost::lexical_cast;
  38. using boost::shared_ptr;
  39. using boost::dynamic_pointer_cast;
  40. using isc::datasrc::memory::InMemoryClient;
  41. using isc::datasrc::memory::ZoneTableSegment;
  42. using isc::datasrc::memory::ZoneDataUpdater;
  43. namespace isc {
  44. namespace datasrc {
  45. ConfigurableClientList::DataSourceInfo::DataSourceInfo(
  46. DataSourceClient* data_src_client,
  47. const DataSourceClientContainerPtr& container,
  48. boost::shared_ptr<internal::CacheConfig> cache_conf,
  49. const RRClass& rrclass, const string& name) :
  50. data_src_client_(data_src_client),
  51. container_(container),
  52. name_(name),
  53. cache_conf_(cache_conf)
  54. {
  55. if (cache_conf_ && cache_conf_->isEnabled()) {
  56. ztable_segment_.reset(ZoneTableSegment::create(
  57. rrclass, cache_conf_->getSegmentType()));
  58. cache_.reset(new InMemoryClient(ztable_segment_, rrclass));
  59. }
  60. }
  61. const DataSourceClient*
  62. ConfigurableClientList::DataSourceInfo::getCacheClient() const {
  63. return (cache_.get());
  64. }
  65. ConfigurableClientList::ConfigurableClientList(const RRClass& rrclass) :
  66. rrclass_(rrclass),
  67. configuration_(new isc::data::ListElement),
  68. allow_cache_(false)
  69. {}
  70. void
  71. ConfigurableClientList::configure(const ConstElementPtr& config,
  72. bool allow_cache)
  73. {
  74. if (!config) {
  75. isc_throw(isc::BadValue, "NULL configuration passed");
  76. }
  77. // TODO: Implement recycling from the old configuration.
  78. size_t i(0); // Outside of the try to be able to access it in the catch
  79. try {
  80. vector<DataSourceInfo> new_data_sources;
  81. set<string> used_names;
  82. for (; i < config->size(); ++i) {
  83. // Extract the parameters
  84. const ConstElementPtr dconf(config->get(i));
  85. const ConstElementPtr type_elem(dconf->get("type"));
  86. if (type_elem == ConstElementPtr()) {
  87. isc_throw(ConfigurationError, "Missing the type option in "
  88. "data source no " << i);
  89. }
  90. const string type(type_elem->stringValue());
  91. ConstElementPtr param_conf(dconf->get("params"));
  92. if (param_conf == ConstElementPtr()) {
  93. param_conf.reset(new NullElement());
  94. }
  95. // Get the name (either explicit, or guess)
  96. const ConstElementPtr name_elem(dconf->get("name"));
  97. const string datasrc_name =
  98. name_elem ? name_elem->stringValue() : type;
  99. if (!used_names.insert(datasrc_name).second) {
  100. isc_throw(ConfigurationError, "Duplicate name in client list: "
  101. << datasrc_name);
  102. }
  103. DataSourcePair dsrc_pair;
  104. try {
  105. // Create a client for the underling data source via
  106. // factory. If it's our internal type of data source,
  107. // this is essentially no-op. In the latter case, it's
  108. // of no use unless cache is allowed; we simply skip
  109. // building it in that case.
  110. dsrc_pair = getDataSourceClient(type, param_conf);
  111. } catch (const DataSourceLibraryError& ex) {
  112. LOG_WARN(logger, DATASRC_LIBRARY_FAILURE).
  113. arg(datasrc_name).arg(rrclass_).arg(ex.what());
  114. continue;
  115. }
  116. if (!allow_cache && !dsrc_pair.first) {
  117. LOG_WARN(logger, DATASRC_LIST_NOT_CACHED).
  118. arg(datasrc_name).arg(rrclass_);
  119. continue;
  120. }
  121. // Build in-memory cache configuration, and create a set of
  122. // related objects including the in-memory zone table for the
  123. // cache.
  124. boost::shared_ptr<internal::CacheConfig> cache_conf(
  125. new internal::CacheConfig(type, dsrc_pair.first, *dconf,
  126. allow_cache));
  127. new_data_sources.push_back(DataSourceInfo(dsrc_pair.first,
  128. dsrc_pair.second,
  129. cache_conf, rrclass_,
  130. datasrc_name));
  131. // If cache is disabled, or the zone table segment is not (yet)
  132. // writable, we are done for this data source.
  133. // Otherwise load zones into the in-memory cache.
  134. if (!cache_conf->isEnabled()) {
  135. continue;
  136. }
  137. memory::ZoneTableSegment& zt_segment =
  138. *new_data_sources.back().ztable_segment_;
  139. if (!zt_segment.isWritable()) {
  140. LOG_DEBUG(logger, DBGLVL_TRACE_BASIC,
  141. DATASRC_LIST_CACHE_PENDING).arg(datasrc_name);
  142. continue;
  143. }
  144. internal::CacheConfig::ConstZoneIterator end_of_zones =
  145. cache_conf->end();
  146. for (internal::CacheConfig::ConstZoneIterator zone_it =
  147. cache_conf->begin();
  148. zone_it != end_of_zones;
  149. ++zone_it)
  150. {
  151. const Name& zname = zone_it->first;
  152. try {
  153. const memory::LoadAction load_action =
  154. cache_conf->getLoadAction(rrclass_, zname);
  155. // in this loop this should be always true
  156. assert(load_action);
  157. // For the initial load, we'll let the writer handle
  158. // loading error and install an empty zone in the table.
  159. memory::ZoneWriter writer(zt_segment, load_action, zname,
  160. rrclass_, true);
  161. std::string error_msg;
  162. writer.load(&error_msg);
  163. if (!error_msg.empty()) {
  164. LOG_ERROR(logger, DATASRC_LOAD_ZONE_ERROR).arg(zname).
  165. arg(rrclass_).arg(datasrc_name).arg(error_msg);
  166. }
  167. writer.install();
  168. writer.cleanup();
  169. } catch (const NoSuchZone&) {
  170. LOG_ERROR(logger, DATASRC_CACHE_ZONE_NOTFOUND).
  171. arg(zname).arg(rrclass_).arg(datasrc_name);
  172. }
  173. }
  174. }
  175. // If everything is OK up until now, we have the new configuration
  176. // ready. So just put it there and let the old one die when we exit
  177. // the scope.
  178. data_sources_.swap(new_data_sources);
  179. configuration_ = config;
  180. allow_cache_ = allow_cache;
  181. } catch (const TypeError& te) {
  182. isc_throw(ConfigurationError, "Malformed configuration at data source "
  183. "no. " << i << ": " << te.what());
  184. } catch (const internal::CacheConfigError& ex) {
  185. // convert to the "public" exception type.
  186. isc_throw(ConfigurationError, ex.what());
  187. }
  188. }
  189. namespace {
  190. class CacheKeeper : public ClientList::FindResult::LifeKeeper {
  191. public:
  192. CacheKeeper(const boost::shared_ptr<InMemoryClient>& cache) :
  193. cache_(cache)
  194. {}
  195. private:
  196. const boost::shared_ptr<InMemoryClient> cache_;
  197. };
  198. class ContainerKeeper : public ClientList::FindResult::LifeKeeper {
  199. public:
  200. ContainerKeeper(const DataSourceClientContainerPtr& container) :
  201. container_(container)
  202. {}
  203. private:
  204. const DataSourceClientContainerPtr container_;
  205. };
  206. boost::shared_ptr<ClientList::FindResult::LifeKeeper>
  207. genKeeper(const ConfigurableClientList::DataSourceInfo* info) {
  208. if (info == NULL) {
  209. return (boost::shared_ptr<ClientList::FindResult::LifeKeeper>());
  210. }
  211. if (info->cache_) {
  212. return (boost::shared_ptr<ClientList::FindResult::LifeKeeper>(
  213. new CacheKeeper(info->cache_)));
  214. } else {
  215. return (boost::shared_ptr<ClientList::FindResult::LifeKeeper>(
  216. new ContainerKeeper(info->container_)));
  217. }
  218. }
  219. }
  220. // We have this class as a temporary storage, as the FindResult can't be
  221. // assigned.
  222. struct ConfigurableClientList::MutableResult {
  223. MutableResult() :
  224. datasrc_client(NULL),
  225. matched_labels(0),
  226. matched(false),
  227. exact(false),
  228. info(NULL)
  229. {}
  230. DataSourceClient* datasrc_client;
  231. ZoneFinderPtr finder;
  232. uint8_t matched_labels;
  233. bool matched;
  234. bool exact;
  235. const DataSourceInfo* info;
  236. operator FindResult() const {
  237. // Conversion to the right result.
  238. return (FindResult(datasrc_client, finder, exact, genKeeper(info)));
  239. }
  240. };
  241. ClientList::FindResult
  242. ConfigurableClientList::find(const dns::Name& name, bool want_exact_match,
  243. bool want_finder) const
  244. {
  245. MutableResult result;
  246. findInternal(result, name, want_exact_match, want_finder);
  247. return (result);
  248. }
  249. void
  250. ConfigurableClientList::findInternal(MutableResult& candidate,
  251. const dns::Name& name,
  252. bool want_exact_match, bool) const
  253. {
  254. BOOST_FOREACH(const DataSourceInfo& info, data_sources_) {
  255. DataSourceClient* client(info.cache_ ? info.cache_.get() :
  256. info.data_src_client_);
  257. const DataSourceClient::FindResult result(client->findZone(name));
  258. // TODO: Once we mark the zones that are not loaded, but are present
  259. // in the data source somehow, check them too.
  260. switch (result.code) {
  261. case result::SUCCESS:
  262. // If we found an exact match, we have no hope to getting
  263. // a better one. Stop right here.
  264. // TODO: In case we have only the datasource and not the finder
  265. // and the need_updater parameter is true, get the zone there.
  266. candidate.datasrc_client = client;
  267. candidate.finder = result.zone_finder;
  268. candidate.matched = true;
  269. candidate.exact = true;
  270. candidate.info = &info;
  271. return;
  272. case result::PARTIALMATCH:
  273. if (!want_exact_match) {
  274. // In case we have a partial match, check if it is better
  275. // than what we have. If so, replace it.
  276. //
  277. // We don't need the labels at the first partial match,
  278. // we have nothing to compare with. So we don't get it
  279. // (as a performance) and hope we will not need it at all.
  280. const uint8_t labels(candidate.matched ?
  281. result.zone_finder->getOrigin().getLabelCount() : 0);
  282. if (candidate.matched && candidate.matched_labels == 0) {
  283. // But if the hope turns out to be false, we need to
  284. // compute it for the first match anyway.
  285. candidate.matched_labels = candidate.finder->
  286. getOrigin().getLabelCount();
  287. }
  288. if (labels > candidate.matched_labels ||
  289. !candidate.matched) {
  290. // This one is strictly better. Replace it.
  291. candidate.datasrc_client = client;
  292. candidate.finder = result.zone_finder;
  293. candidate.matched_labels = labels;
  294. candidate.matched = true;
  295. candidate.info = &info;
  296. }
  297. }
  298. break;
  299. default:
  300. // Nothing found, nothing to do.
  301. break;
  302. }
  303. }
  304. // TODO: In case we have only the datasource and not the finder
  305. // and the need_updater parameter is true, get the zone there.
  306. }
  307. void
  308. ConfigurableClientList::resetMemorySegment
  309. (const std::string& datasrc_name,
  310. ZoneTableSegment::MemorySegmentOpenMode mode,
  311. ConstElementPtr config_params)
  312. {
  313. BOOST_FOREACH(DataSourceInfo& info, data_sources_) {
  314. if (info.name_ == datasrc_name) {
  315. ZoneTableSegment& segment = *info.ztable_segment_;
  316. segment.reset(mode, config_params);
  317. break;
  318. }
  319. }
  320. }
  321. ConfigurableClientList::ZoneWriterPair
  322. ConfigurableClientList::getCachedZoneWriter(const Name& name,
  323. const std::string& datasrc_name)
  324. {
  325. if (!allow_cache_) {
  326. return (ZoneWriterPair(CACHE_DISABLED, ZoneWriterPtr()));
  327. }
  328. // Find the data source from which the zone to be loaded into memory.
  329. // Then get the appropriate load action and create a zone writer.
  330. BOOST_FOREACH(const DataSourceInfo& info, data_sources_) {
  331. if (!datasrc_name.empty() && datasrc_name != info.name_) {
  332. continue;
  333. }
  334. // If there's an underlying "real" data source and it doesn't contain
  335. // the given name, obviously we cannot load it. If a specific data
  336. // source is given by the name, search should stop here.
  337. if (info.data_src_client_ &&
  338. info.data_src_client_->findZone(name).code != result::SUCCESS) {
  339. if (!datasrc_name.empty()) {
  340. return (ZoneWriterPair(ZONE_NOT_FOUND, ZoneWriterPtr()));
  341. }
  342. continue;
  343. }
  344. // If the corresponding zone table segment is not (yet) writable,
  345. // we cannot load at this time.
  346. if (info.ztable_segment_ && !info.ztable_segment_->isWritable()) {
  347. return (ZoneWriterPair(CACHE_NOT_WRITABLE, ZoneWriterPtr()));
  348. }
  349. // Note that getCacheConfig() must return non NULL in this module
  350. // (only tests could set it to a bogus value).
  351. const memory::LoadAction load_action =
  352. info.getCacheConfig()->getLoadAction(rrclass_, name);
  353. if (!load_action) {
  354. return (ZoneWriterPair(ZONE_NOT_CACHED, ZoneWriterPtr()));
  355. }
  356. return (ZoneWriterPair(ZONE_SUCCESS,
  357. ZoneWriterPtr(
  358. new memory::ZoneWriter(
  359. *info.ztable_segment_,
  360. load_action, name, rrclass_, false))));
  361. }
  362. // We can't find the specified zone. If a specific data source was
  363. // given, this means the given name of data source doesn't exist, so
  364. // we report it so.
  365. if (!datasrc_name.empty()) {
  366. return (ZoneWriterPair(DATASRC_NOT_FOUND, ZoneWriterPtr()));
  367. }
  368. return (ZoneWriterPair(ZONE_NOT_FOUND, ZoneWriterPtr()));
  369. }
  370. // NOTE: This function is not tested, it would be complicated. However, the
  371. // purpose of the function is to provide a very thin wrapper to be able to
  372. // replace the call to DataSourceClientContainer constructor in tests.
  373. ConfigurableClientList::DataSourcePair
  374. ConfigurableClientList::getDataSourceClient(const string& type,
  375. const ConstElementPtr&
  376. configuration)
  377. {
  378. if (type == "MasterFiles") {
  379. return (DataSourcePair(0, DataSourceClientContainerPtr()));
  380. }
  381. DataSourceClientContainerPtr
  382. container(new DataSourceClientContainer(type, configuration));
  383. return (DataSourcePair(&container->getInstance(), container));
  384. }
  385. vector<DataSourceStatus>
  386. ConfigurableClientList::getStatus() const {
  387. vector<DataSourceStatus> result;
  388. BOOST_FOREACH(const DataSourceInfo& info, data_sources_) {
  389. if (info.ztable_segment_) {
  390. result.push_back(DataSourceStatus(
  391. info.name_,
  392. (info.ztable_segment_->isUsable() ?
  393. SEGMENT_INUSE : SEGMENT_WAITING),
  394. info.ztable_segment_->getImplType()));
  395. } else {
  396. result.push_back(DataSourceStatus(info.name_));
  397. }
  398. }
  399. return (result);
  400. }
  401. ConstZoneTableAccessorPtr
  402. ConfigurableClientList::getZoneTableAccessor(const std::string& datasrc_name,
  403. bool use_cache) const
  404. {
  405. if (!use_cache) {
  406. isc_throw(isc::NotImplemented,
  407. "getZoneTableAccessor only implemented for cache");
  408. }
  409. // Find the matching data source
  410. BOOST_FOREACH(const DataSourceInfo& info, data_sources_) {
  411. if (!datasrc_name.empty() && datasrc_name != info.name_) {
  412. continue;
  413. }
  414. const internal::CacheConfig* config(info.getCacheConfig());
  415. // If caching is disabled for the named data source, this will
  416. // return an accessor to an effectivley empty table.
  417. return (ConstZoneTableAccessorPtr
  418. (new internal::ZoneTableAccessorCache(*config)));
  419. }
  420. return (ConstZoneTableAccessorPtr());
  421. }
  422. }
  423. }