client_list.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  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. // Create a client for the underling data source via factory.
  104. // If it's our internal type of data source, this is essentially
  105. // no-op. In the latter case, it's of no use unless cache is
  106. // allowed; we simply skip building it in that case.
  107. const DataSourcePair dsrc_pair = getDataSourceClient(type,
  108. param_conf);
  109. if (!allow_cache && !dsrc_pair.first) {
  110. LOG_WARN(logger, DATASRC_LIST_NOT_CACHED).
  111. arg(datasrc_name).arg(rrclass_);
  112. continue;
  113. }
  114. // Build in-memory cache configuration, and create a set of
  115. // related objects including the in-memory zone table for the
  116. // cache.
  117. boost::shared_ptr<internal::CacheConfig> cache_conf(
  118. new internal::CacheConfig(type, dsrc_pair.first, *dconf,
  119. allow_cache));
  120. new_data_sources.push_back(DataSourceInfo(dsrc_pair.first,
  121. dsrc_pair.second,
  122. cache_conf, rrclass_,
  123. datasrc_name));
  124. // If cache is disabled, or the zone table segment is not (yet)
  125. // writable, we are done for this data source.
  126. // Otherwise load zones into the in-memory cache.
  127. if (!cache_conf->isEnabled()) {
  128. continue;
  129. }
  130. memory::ZoneTableSegment& zt_segment =
  131. *new_data_sources.back().ztable_segment_;
  132. if (!zt_segment.isWritable()) {
  133. LOG_DEBUG(logger, DBGLVL_TRACE_BASIC,
  134. DATASRC_LIST_CACHE_PENDING).arg(datasrc_name);
  135. continue;
  136. }
  137. internal::CacheConfig::ConstZoneIterator end_of_zones =
  138. cache_conf->end();
  139. for (internal::CacheConfig::ConstZoneIterator zone_it =
  140. cache_conf->begin();
  141. zone_it != end_of_zones;
  142. ++zone_it)
  143. {
  144. const Name& zname = zone_it->first;
  145. try {
  146. const memory::LoadAction load_action =
  147. cache_conf->getLoadAction(rrclass_, zname);
  148. // in this loop this should be always true
  149. assert(load_action);
  150. // For the initial load, we'll let the writer handle
  151. // loading error and install an empty zone in the table.
  152. memory::ZoneWriter writer(zt_segment, load_action, zname,
  153. rrclass_, true);
  154. std::string error_msg;
  155. writer.load(&error_msg);
  156. if (!error_msg.empty()) {
  157. LOG_ERROR(logger, DATASRC_LOAD_ZONE_ERROR).arg(zname).
  158. arg(rrclass_).arg(datasrc_name).arg(error_msg);
  159. }
  160. writer.install();
  161. writer.cleanup();
  162. } catch (const NoSuchZone&) {
  163. LOG_ERROR(logger, DATASRC_CACHE_ZONE_NOTFOUND).
  164. arg(zname).arg(rrclass_).arg(datasrc_name);
  165. }
  166. }
  167. }
  168. // If everything is OK up until now, we have the new configuration
  169. // ready. So just put it there and let the old one die when we exit
  170. // the scope.
  171. data_sources_.swap(new_data_sources);
  172. configuration_ = config;
  173. allow_cache_ = allow_cache;
  174. } catch (const TypeError& te) {
  175. isc_throw(ConfigurationError, "Malformed configuration at data source "
  176. "no. " << i << ": " << te.what());
  177. } catch (const internal::CacheConfigError& ex) {
  178. // convert to the "public" exception type.
  179. isc_throw(ConfigurationError, ex.what());
  180. }
  181. }
  182. namespace {
  183. class CacheKeeper : public ClientList::FindResult::LifeKeeper {
  184. public:
  185. CacheKeeper(const boost::shared_ptr<InMemoryClient>& cache) :
  186. cache_(cache)
  187. {}
  188. private:
  189. const boost::shared_ptr<InMemoryClient> cache_;
  190. };
  191. class ContainerKeeper : public ClientList::FindResult::LifeKeeper {
  192. public:
  193. ContainerKeeper(const DataSourceClientContainerPtr& container) :
  194. container_(container)
  195. {}
  196. private:
  197. const DataSourceClientContainerPtr container_;
  198. };
  199. boost::shared_ptr<ClientList::FindResult::LifeKeeper>
  200. genKeeper(const ConfigurableClientList::DataSourceInfo* info) {
  201. if (info == NULL) {
  202. return (boost::shared_ptr<ClientList::FindResult::LifeKeeper>());
  203. }
  204. if (info->cache_) {
  205. return (boost::shared_ptr<ClientList::FindResult::LifeKeeper>(
  206. new CacheKeeper(info->cache_)));
  207. } else {
  208. return (boost::shared_ptr<ClientList::FindResult::LifeKeeper>(
  209. new ContainerKeeper(info->container_)));
  210. }
  211. }
  212. }
  213. // We have this class as a temporary storage, as the FindResult can't be
  214. // assigned.
  215. struct ConfigurableClientList::MutableResult {
  216. MutableResult() :
  217. datasrc_client(NULL),
  218. matched_labels(0),
  219. matched(false),
  220. exact(false),
  221. info(NULL)
  222. {}
  223. DataSourceClient* datasrc_client;
  224. ZoneFinderPtr finder;
  225. uint8_t matched_labels;
  226. bool matched;
  227. bool exact;
  228. const DataSourceInfo* info;
  229. operator FindResult() const {
  230. // Conversion to the right result.
  231. return (FindResult(datasrc_client, finder, exact, genKeeper(info)));
  232. }
  233. };
  234. ClientList::FindResult
  235. ConfigurableClientList::find(const dns::Name& name, bool want_exact_match,
  236. bool want_finder) const
  237. {
  238. MutableResult result;
  239. findInternal(result, name, want_exact_match, want_finder);
  240. return (result);
  241. }
  242. void
  243. ConfigurableClientList::findInternal(MutableResult& candidate,
  244. const dns::Name& name,
  245. bool want_exact_match, bool) const
  246. {
  247. BOOST_FOREACH(const DataSourceInfo& info, data_sources_) {
  248. DataSourceClient* client(info.cache_ ? info.cache_.get() :
  249. info.data_src_client_);
  250. const DataSourceClient::FindResult result(client->findZone(name));
  251. // TODO: Once we mark the zones that are not loaded, but are present
  252. // in the data source somehow, check them too.
  253. switch (result.code) {
  254. case result::SUCCESS:
  255. // If we found an exact match, we have no hope to getting
  256. // a better one. Stop right here.
  257. // TODO: In case we have only the datasource and not the finder
  258. // and the need_updater parameter is true, get the zone there.
  259. candidate.datasrc_client = client;
  260. candidate.finder = result.zone_finder;
  261. candidate.matched = true;
  262. candidate.exact = true;
  263. candidate.info = &info;
  264. return;
  265. case result::PARTIALMATCH:
  266. if (!want_exact_match) {
  267. // In case we have a partial match, check if it is better
  268. // than what we have. If so, replace it.
  269. //
  270. // We don't need the labels at the first partial match,
  271. // we have nothing to compare with. So we don't get it
  272. // (as a performance) and hope we will not need it at all.
  273. const uint8_t labels(candidate.matched ?
  274. result.zone_finder->getOrigin().getLabelCount() : 0);
  275. if (candidate.matched && candidate.matched_labels == 0) {
  276. // But if the hope turns out to be false, we need to
  277. // compute it for the first match anyway.
  278. candidate.matched_labels = candidate.finder->
  279. getOrigin().getLabelCount();
  280. }
  281. if (labels > candidate.matched_labels ||
  282. !candidate.matched) {
  283. // This one is strictly better. Replace it.
  284. candidate.datasrc_client = client;
  285. candidate.finder = result.zone_finder;
  286. candidate.matched_labels = labels;
  287. candidate.matched = true;
  288. candidate.info = &info;
  289. }
  290. }
  291. break;
  292. default:
  293. // Nothing found, nothing to do.
  294. break;
  295. }
  296. }
  297. // TODO: In case we have only the datasource and not the finder
  298. // and the need_updater parameter is true, get the zone there.
  299. }
  300. ConfigurableClientList::ZoneWriterPair
  301. ConfigurableClientList::getCachedZoneWriter(const Name& name,
  302. const std::string& datasrc_name)
  303. {
  304. if (!allow_cache_) {
  305. return (ZoneWriterPair(CACHE_DISABLED, ZoneWriterPtr()));
  306. }
  307. // Find the data source from which the zone to be loaded into memory.
  308. // Then get the appropriate load action and create a zone writer.
  309. BOOST_FOREACH(const DataSourceInfo& info, data_sources_) {
  310. if (!datasrc_name.empty() && datasrc_name != info.name_) {
  311. continue;
  312. }
  313. // If there's an underlying "real" data source and it doesn't contain
  314. // the given name, obviously we cannot load it. If a specific data
  315. // source is given by the name, search should stop here.
  316. if (info.data_src_client_ &&
  317. info.data_src_client_->findZone(name).code != result::SUCCESS) {
  318. if (!datasrc_name.empty()) {
  319. return (ZoneWriterPair(ZONE_NOT_FOUND, ZoneWriterPtr()));
  320. }
  321. continue;
  322. }
  323. // If the corresponding zone table segment is not (yet) writable,
  324. // we cannot load at this time.
  325. if (info.ztable_segment_ && !info.ztable_segment_->isWritable()) {
  326. return (ZoneWriterPair(CACHE_NOT_WRITABLE, ZoneWriterPtr()));
  327. }
  328. // Note that getCacheConfig() must return non NULL in this module
  329. // (only tests could set it to a bogus value).
  330. const memory::LoadAction load_action =
  331. info.getCacheConfig()->getLoadAction(rrclass_, name);
  332. if (!load_action) {
  333. return (ZoneWriterPair(ZONE_NOT_CACHED, ZoneWriterPtr()));
  334. }
  335. return (ZoneWriterPair(ZONE_SUCCESS,
  336. ZoneWriterPtr(
  337. new memory::ZoneWriter(
  338. *info.ztable_segment_,
  339. load_action, name, rrclass_, false))));
  340. }
  341. // We can't find the specified zone. If a specific data source was
  342. // given, this means the given name of data source doesn't exist, so
  343. // we report it so.
  344. if (!datasrc_name.empty()) {
  345. return (ZoneWriterPair(DATASRC_NOT_FOUND, ZoneWriterPtr()));
  346. }
  347. return (ZoneWriterPair(ZONE_NOT_FOUND, ZoneWriterPtr()));
  348. }
  349. // NOTE: This function is not tested, it would be complicated. However, the
  350. // purpose of the function is to provide a very thin wrapper to be able to
  351. // replace the call to DataSourceClientContainer constructor in tests.
  352. ConfigurableClientList::DataSourcePair
  353. ConfigurableClientList::getDataSourceClient(const string& type,
  354. const ConstElementPtr&
  355. configuration)
  356. {
  357. if (type == "MasterFiles") {
  358. return (DataSourcePair(0, DataSourceClientContainerPtr()));
  359. }
  360. DataSourceClientContainerPtr
  361. container(new DataSourceClientContainer(type, configuration));
  362. return (DataSourcePair(&container->getInstance(), container));
  363. }
  364. vector<DataSourceStatus>
  365. ConfigurableClientList::getStatus() const {
  366. vector<DataSourceStatus> result;
  367. BOOST_FOREACH(const DataSourceInfo& info, data_sources_) {
  368. // TODO: Once we support mapped cache, decide when we need the
  369. // SEGMENT_WAITING.
  370. result.push_back(DataSourceStatus(info.name_, info.cache_ ?
  371. SEGMENT_INUSE : SEGMENT_UNUSED,
  372. "local"));
  373. }
  374. return (result);
  375. }
  376. ConstZoneTableAccessorPtr
  377. ConfigurableClientList::getZoneTableAccessor(const std::string& datasrc_name,
  378. bool use_cache) const
  379. {
  380. if (!use_cache) {
  381. isc_throw(isc::NotImplemented,
  382. "getZoneTableAccessor only implemented for cache");
  383. }
  384. // Find the matching data source
  385. BOOST_FOREACH(const DataSourceInfo& info, data_sources_) {
  386. if (!datasrc_name.empty() && datasrc_name != info.name_) {
  387. continue;
  388. }
  389. const internal::CacheConfig* config(info.getCacheConfig());
  390. // If caching is disabled for the named data source, this will
  391. // return an accessor to an effectivley empty table.
  392. return (ConstZoneTableAccessorPtr
  393. (new internal::ZoneTableAccessorCache(*config)));
  394. }
  395. return (ConstZoneTableAccessorPtr());
  396. }
  397. }
  398. }