client_list.cc 15 KB

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