client_list.cc 17 KB

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