client_list.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  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 "client_list.h"
  15. #include "client.h"
  16. #include "factory.h"
  17. #include "memory_datasrc.h"
  18. #include <memory>
  19. #include <boost/foreach.hpp>
  20. using namespace isc::data;
  21. using namespace isc::dns;
  22. using namespace std;
  23. using namespace boost;
  24. namespace isc {
  25. namespace datasrc {
  26. ConfigurableClientList::DataSourceInfo::DataSourceInfo(
  27. DataSourceClient* data_src_client,
  28. const DataSourceClientContainerPtr& container, bool has_cache) :
  29. data_src_client_(data_src_client),
  30. container_(container)
  31. {
  32. if (has_cache) {
  33. cache_.reset(new InMemoryClient);
  34. }
  35. }
  36. ConfigurableClientList::DataSourceInfo::DataSourceInfo(bool has_cache) :
  37. data_src_client_(NULL)
  38. {
  39. if (has_cache) {
  40. cache_.reset(new InMemoryClient);
  41. }
  42. }
  43. void
  44. ConfigurableClientList::configure(const ConstElementPtr& config,
  45. bool allow_cache)
  46. {
  47. if (!config) {
  48. isc_throw(isc::BadValue, "NULL configuration passed");
  49. }
  50. // TODO: Implement recycling from the old configuration.
  51. size_t i(0); // Outside of the try to be able to access it in the catch
  52. try {
  53. vector<DataSourceInfo> new_data_sources;
  54. for (; i < config->size(); ++i) {
  55. // Extract the parameters
  56. const ConstElementPtr dconf(config->get(i));
  57. const ConstElementPtr typeElem(dconf->get("type"));
  58. if (typeElem == ConstElementPtr()) {
  59. isc_throw(ConfigurationError, "Missing the type option in "
  60. "data source no " << i);
  61. }
  62. const string type(typeElem->stringValue());
  63. ConstElementPtr paramConf(dconf->get("params"));
  64. if (paramConf == ConstElementPtr()) {
  65. paramConf.reset(new NullElement());
  66. }
  67. const bool want_cache(allow_cache &&
  68. dconf->contains("cache-enable") &&
  69. dconf->get("cache-enable")->boolValue());
  70. if (type == "MasterFiles") {
  71. // In case the cache is not allowed, we just skip the master
  72. // files (at least for now)
  73. if (!allow_cache) {
  74. continue;
  75. }
  76. if (!want_cache) {
  77. isc_throw(ConfigurationError, "The cache must be enabled "
  78. "for the MasterFiles type");
  79. }
  80. new_data_sources.push_back(DataSourceInfo(true));
  81. } else {
  82. // Ask the factory to create the data source for us
  83. const DataSourcePair ds(this->getDataSourceClient(type,
  84. paramConf));
  85. // And put it into the vector
  86. new_data_sources.push_back(DataSourceInfo(ds.first, ds.second,
  87. want_cache));
  88. }
  89. if (want_cache) {
  90. if (!dconf->contains("cache-zones") && type != "MasterFiles") {
  91. isc_throw(isc::NotImplemented, "Auto-detection of zones "
  92. "to cache is not yet implemented, supply "
  93. "cache-zones parameter");
  94. // TODO: Auto-detect list of all zones in the
  95. // data source.
  96. }
  97. // List the zones we are loading
  98. vector<string> zones_origins;
  99. if (type == "MasterFiles") {
  100. const map<string, ConstElementPtr>
  101. zones_files(paramConf->mapValue());
  102. for (map<string, ConstElementPtr>::const_iterator
  103. it(zones_files.begin()); it != zones_files.end();
  104. ++it) {
  105. zones_origins.push_back(it->first);
  106. }
  107. } else {
  108. const ConstElementPtr zones(dconf->get("cache-zones"));
  109. for (size_t i(0); i < zones->size(); ++i) {
  110. zones_origins.push_back(zones->get(i)->stringValue());
  111. }
  112. }
  113. const shared_ptr<InMemoryClient>
  114. cache(new_data_sources.back().cache_);
  115. const DataSourceClient* const
  116. client(new_data_sources.back().data_src_client_);
  117. for (vector<string>::const_iterator it(zones_origins.begin());
  118. it != zones_origins.end(); ++it) {
  119. const Name origin(*it);
  120. shared_ptr<InMemoryZoneFinder>
  121. finder(new
  122. InMemoryZoneFinder(rrclass_, origin));
  123. if (type == "MasterFiles") {
  124. finder->load(paramConf->get(*it)->stringValue());
  125. } else {
  126. ZoneIteratorPtr iterator;
  127. try {
  128. iterator = client->getIterator(origin);
  129. }
  130. catch (const DataSourceError&) {
  131. isc_throw(ConfigurationError, "Unable to cache "
  132. "non-existent zone " << origin);
  133. }
  134. if (!iterator) {
  135. isc_throw(isc::Unexpected, "Got NULL iterator for "
  136. "zone " << origin);
  137. }
  138. finder->load(*iterator);
  139. }
  140. cache->addZone(finder);
  141. }
  142. }
  143. }
  144. // If everything is OK up until now, we have the new configuration
  145. // ready. So just put it there and let the old one die when we exit
  146. // the scope.
  147. data_sources_.swap(new_data_sources);
  148. configuration_ = config;
  149. allow_cache_ = allow_cache;
  150. } catch (const TypeError& te) {
  151. isc_throw(ConfigurationError, "Malformed configuration at data source "
  152. "no. " << i << ": " << te.what());
  153. }
  154. }
  155. // We have this class as a temporary storage, as the FindResult can't be
  156. // assigned.
  157. struct ConfigurableClientList::MutableResult {
  158. MutableResult() :
  159. datasrc_client(NULL),
  160. matched_labels(0),
  161. matched(false),
  162. exact(false),
  163. info(NULL)
  164. {}
  165. DataSourceClient* datasrc_client;
  166. ZoneFinderPtr finder;
  167. uint8_t matched_labels;
  168. bool matched;
  169. bool exact;
  170. const DataSourceInfo* info;
  171. operator FindResult() const {
  172. // Conversion to the right result.
  173. return (FindResult(datasrc_client, finder, exact));
  174. }
  175. };
  176. ClientList::FindResult
  177. ConfigurableClientList::find(const dns::Name& name, bool want_exact_match,
  178. bool want_finder) const
  179. {
  180. MutableResult result;
  181. findInternal(result, name, want_exact_match, want_finder);
  182. return (result);
  183. }
  184. void
  185. ConfigurableClientList::findInternal(MutableResult& candidate,
  186. const dns::Name& name,
  187. bool want_exact_match, bool) const
  188. {
  189. BOOST_FOREACH(const DataSourceInfo& info, data_sources_) {
  190. DataSourceClient* client(info.cache_ ? info.cache_.get() :
  191. info.data_src_client_);
  192. const DataSourceClient::FindResult result(client->findZone(name));
  193. // TODO: Once we mark the zones that are not loaded, but are present
  194. // in the data source somehow, check them too.
  195. switch (result.code) {
  196. case result::SUCCESS:
  197. // If we found an exact match, we have no hope to getting
  198. // a better one. Stop right here.
  199. // TODO: In case we have only the datasource and not the finder
  200. // and the need_updater parameter is true, get the zone there.
  201. candidate.datasrc_client = client;
  202. candidate.finder = result.zone_finder;
  203. candidate.matched = true;
  204. candidate.exact = true;
  205. candidate.info = &info;
  206. return;
  207. case result::PARTIALMATCH:
  208. if (!want_exact_match) {
  209. // In case we have a partial match, check if it is better
  210. // than what we have. If so, replace it.
  211. //
  212. // We don't need the labels at the first partial match,
  213. // we have nothing to compare with. So we don't get it
  214. // (as a performance) and hope we will not need it at all.
  215. const uint8_t labels(candidate.matched ?
  216. result.zone_finder->getOrigin().getLabelCount() : 0);
  217. if (candidate.matched && candidate.matched_labels == 0) {
  218. // But if the hope turns out to be false, we need to
  219. // compute it for the first match anyway.
  220. candidate.matched_labels = candidate.finder->
  221. getOrigin().getLabelCount();
  222. }
  223. if (labels > candidate.matched_labels ||
  224. !candidate.matched) {
  225. // This one is strictly better. Replace it.
  226. candidate.datasrc_client = client;
  227. candidate.finder = result.zone_finder;
  228. candidate.matched_labels = labels;
  229. candidate.matched = true;
  230. candidate.info = &info;
  231. }
  232. }
  233. break;
  234. default:
  235. // Nothing found, nothing to do.
  236. break;
  237. }
  238. }
  239. // TODO: In case we have only the datasource and not the finder
  240. // and the need_updater parameter is true, get the zone there.
  241. // Return the partial match we have. In case we didn't want a partial
  242. // match, this surely contains the original empty result.
  243. }
  244. ConfigurableClientList::ReloadResult
  245. ConfigurableClientList::reload(const Name& name) {
  246. if (!allow_cache_) {
  247. return (CACHE_DISABLED);
  248. }
  249. // Try to find the correct zone.
  250. MutableResult result;
  251. findInternal(result, name, true, true);
  252. if (!result.finder) {
  253. return (ZONE_NOT_FOUND);
  254. }
  255. // Try to convert the finder to in-memory one. If it is the cache,
  256. // it should work.
  257. shared_ptr<InMemoryZoneFinder>
  258. finder(dynamic_pointer_cast<InMemoryZoneFinder>(result.finder));
  259. const DataSourceInfo* info(result.info);
  260. // It is of a different type or there's no cache.
  261. if (!info->cache_ || !finder) {
  262. return (ZONE_NOT_CACHED);
  263. }
  264. DataSourceClient* client(info->data_src_client_);
  265. if (client) {
  266. // Now do the final reload. If it does not exist in client,
  267. // DataSourceError is thrown, which is exactly the result what we
  268. // want, so no need to handle it.
  269. ZoneIteratorPtr iterator(client->getIterator(name));
  270. if (!iterator) {
  271. isc_throw(isc::Unexpected, "Null iterator from " << name);
  272. }
  273. finder->load(*iterator);
  274. } else {
  275. // The MasterFiles special case
  276. const string filename(finder->getFileName());
  277. if (filename.empty()) {
  278. isc_throw(isc::Unexpected, "Confused about missing both filename "
  279. "and data source");
  280. }
  281. finder->load(filename);
  282. }
  283. return (ZONE_RELOADED);
  284. }
  285. // NOTE: This function is not tested, it would be complicated. However, the
  286. // purpose of the function is to provide a very thin wrapper to be able to
  287. // replace the call to DataSourceClientContainer constructor in tests.
  288. ConfigurableClientList::DataSourcePair
  289. ConfigurableClientList::getDataSourceClient(const string& type,
  290. const ConstElementPtr&
  291. configuration)
  292. {
  293. DataSourceClientContainerPtr
  294. container(new DataSourceClientContainer(type, configuration));
  295. return (DataSourcePair(&container->getInstance(), container));
  296. }
  297. }
  298. }