client_list.cc 16 KB

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