client_list.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  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 <util/memory_segment_local.h>
  15. #include "client_list.h"
  16. #include "client.h"
  17. #include "factory.h"
  18. #include "memory/memory_client.h"
  19. #include "logger.h"
  20. #include <dns/masterload.h>
  21. #include <memory>
  22. #include <boost/foreach.hpp>
  23. using namespace isc::data;
  24. using namespace isc::dns;
  25. using namespace std;
  26. using isc::util::MemorySegment;
  27. using boost::lexical_cast;
  28. using boost::shared_ptr;
  29. using boost::dynamic_pointer_cast;
  30. using isc::datasrc::memory::InMemoryClient;
  31. namespace isc {
  32. namespace datasrc {
  33. ConfigurableClientList::DataSourceInfo::DataSourceInfo(
  34. DataSourceClient* data_src_client,
  35. const DataSourceClientContainerPtr& container, bool has_cache,
  36. const RRClass& rrclass, MemorySegment& mem_sgmt) :
  37. data_src_client_(data_src_client),
  38. container_(container)
  39. {
  40. if (has_cache) {
  41. cache_.reset(new InMemoryClient(mem_sgmt, rrclass));
  42. }
  43. }
  44. ConfigurableClientList::DataSourceInfo::DataSourceInfo(
  45. const RRClass& rrclass, MemorySegment& mem_sgmt, bool has_cache) :
  46. data_src_client_(NULL)
  47. {
  48. if (has_cache) {
  49. cache_.reset(new InMemoryClient(mem_sgmt, rrclass));
  50. }
  51. }
  52. const DataSourceClient*
  53. ConfigurableClientList::DataSourceInfo::getCacheClient() const {
  54. return (cache_.get());
  55. }
  56. ConfigurableClientList::ConfigurableClientList(const RRClass& rrclass) :
  57. rrclass_(rrclass),
  58. mem_sgmt_(new util::MemorySegmentLocal),
  59. configuration_(new isc::data::ListElement),
  60. allow_cache_(false)
  61. {}
  62. ConfigurableClientList::~ConfigurableClientList() {
  63. // Explicitly clear the contained data source clients, and check memory
  64. // leak. assert() (with abort on failure) may be too harsh, but
  65. // it's probably better to find more leaks initially. Once it's stabilized
  66. // we should probably revisit it.
  67. data_sources_.clear();
  68. assert(mem_sgmt_->allMemoryDeallocated());
  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. 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. const bool want_cache(allow_cache &&
  95. dconf->contains("cache-enable") &&
  96. dconf->get("cache-enable")->boolValue());
  97. if (type == "MasterFiles") {
  98. // In case the cache is not allowed, we just skip the master
  99. // files (at least for now)
  100. if (!allow_cache) {
  101. // We're not going to load these zones. Issue warnings about it.
  102. const map<string, ConstElementPtr>
  103. zones_files(paramConf->mapValue());
  104. for (map<string, ConstElementPtr>::const_iterator
  105. it(zones_files.begin()); it != zones_files.end();
  106. ++it) {
  107. LOG_WARN(logger, DATASRC_LIST_NOT_CACHED).
  108. arg(it->first).arg(rrclass_);
  109. }
  110. continue;
  111. }
  112. if (!want_cache) {
  113. isc_throw(ConfigurationError, "The cache must be enabled "
  114. "for the MasterFiles type");
  115. }
  116. new_data_sources.push_back(DataSourceInfo(rrclass_, *mem_sgmt_,
  117. true));
  118. } else {
  119. // Ask the factory to create the data source for us
  120. const DataSourcePair ds(this->getDataSourceClient(type,
  121. paramConf));
  122. // And put it into the vector
  123. new_data_sources.push_back(DataSourceInfo(ds.first, ds.second,
  124. want_cache, rrclass_,
  125. *mem_sgmt_));
  126. }
  127. if (want_cache) {
  128. if (!dconf->contains("cache-zones") && type != "MasterFiles") {
  129. isc_throw(isc::NotImplemented, "Auto-detection of zones "
  130. "to cache is not yet implemented, supply "
  131. "cache-zones parameter");
  132. // TODO: Auto-detect list of all zones in the
  133. // data source.
  134. }
  135. // List the zones we are loading
  136. vector<string> zones_origins;
  137. if (type == "MasterFiles") {
  138. const map<string, ConstElementPtr>
  139. zones_files(paramConf->mapValue());
  140. for (map<string, ConstElementPtr>::const_iterator
  141. it(zones_files.begin()); it != zones_files.end();
  142. ++it) {
  143. zones_origins.push_back(it->first);
  144. }
  145. } else {
  146. const ConstElementPtr zones(dconf->get("cache-zones"));
  147. for (size_t i(0); i < zones->size(); ++i) {
  148. zones_origins.push_back(zones->get(i)->stringValue());
  149. }
  150. }
  151. const shared_ptr<InMemoryClient>
  152. cache(new_data_sources.back().cache_);
  153. const DataSourceClient* const
  154. client(new_data_sources.back().data_src_client_);
  155. for (vector<string>::const_iterator it(zones_origins.begin());
  156. it != zones_origins.end(); ++it) {
  157. const Name origin(*it);
  158. if (type == "MasterFiles") {
  159. try {
  160. cache->load(origin,
  161. paramConf->get(*it)->stringValue());
  162. } catch (const isc::dns::MasterLoadError& mle) {
  163. LOG_ERROR(logger, DATASRC_MASTERLOAD_ERROR)
  164. .arg(mle.what());
  165. }
  166. } else {
  167. ZoneIteratorPtr iterator;
  168. try {
  169. iterator = client->getIterator(origin);
  170. } catch (const DataSourceError&) {
  171. isc_throw(ConfigurationError, "Unable to "
  172. "cache non-existent zone "
  173. << origin);
  174. }
  175. if (!iterator) {
  176. isc_throw(isc::Unexpected, "Got NULL iterator "
  177. "for zone " << origin);
  178. }
  179. cache->load(origin, *iterator);
  180. }
  181. }
  182. }
  183. }
  184. // If everything is OK up until now, we have the new configuration
  185. // ready. So just put it there and let the old one die when we exit
  186. // the scope.
  187. data_sources_.swap(new_data_sources);
  188. configuration_ = config;
  189. allow_cache_ = allow_cache;
  190. } catch (const TypeError& te) {
  191. isc_throw(ConfigurationError, "Malformed configuration at data source "
  192. "no. " << i << ": " << te.what());
  193. }
  194. }
  195. namespace {
  196. class CacheKeeper : public ClientList::FindResult::LifeKeeper {
  197. public:
  198. CacheKeeper(const boost::shared_ptr<InMemoryClient>& cache) :
  199. cache_(cache)
  200. {}
  201. private:
  202. const boost::shared_ptr<InMemoryClient> cache_;
  203. };
  204. class ContainerKeeper : public ClientList::FindResult::LifeKeeper {
  205. public:
  206. ContainerKeeper(const DataSourceClientContainerPtr& container) :
  207. container_(container)
  208. {}
  209. private:
  210. const DataSourceClientContainerPtr container_;
  211. };
  212. boost::shared_ptr<ClientList::FindResult::LifeKeeper>
  213. genKeeper(const ConfigurableClientList::DataSourceInfo* info) {
  214. if (info == NULL) {
  215. return (boost::shared_ptr<ClientList::FindResult::LifeKeeper>());
  216. }
  217. if (info->cache_) {
  218. return (boost::shared_ptr<ClientList::FindResult::LifeKeeper>(
  219. new CacheKeeper(info->cache_)));
  220. } else {
  221. return (boost::shared_ptr<ClientList::FindResult::LifeKeeper>(
  222. new ContainerKeeper(info->container_)));
  223. }
  224. }
  225. }
  226. // We have this class as a temporary storage, as the FindResult can't be
  227. // assigned.
  228. struct ConfigurableClientList::MutableResult {
  229. MutableResult() :
  230. datasrc_client(NULL),
  231. matched_labels(0),
  232. matched(false),
  233. exact(false),
  234. info(NULL)
  235. {}
  236. DataSourceClient* datasrc_client;
  237. ZoneFinderPtr finder;
  238. uint8_t matched_labels;
  239. bool matched;
  240. bool exact;
  241. const DataSourceInfo* info;
  242. operator FindResult() const {
  243. // Conversion to the right result.
  244. return (FindResult(datasrc_client, finder, exact, genKeeper(info)));
  245. }
  246. };
  247. ClientList::FindResult
  248. ConfigurableClientList::find(const dns::Name& name, bool want_exact_match,
  249. bool want_finder) const
  250. {
  251. MutableResult result;
  252. findInternal(result, name, want_exact_match, want_finder);
  253. return (result);
  254. }
  255. void
  256. ConfigurableClientList::findInternal(MutableResult& candidate,
  257. const dns::Name& name,
  258. bool want_exact_match, bool) const
  259. {
  260. BOOST_FOREACH(const DataSourceInfo& info, data_sources_) {
  261. DataSourceClient* client(info.cache_ ? info.cache_.get() :
  262. info.data_src_client_);
  263. const DataSourceClient::FindResult result(client->findZone(name));
  264. // TODO: Once we mark the zones that are not loaded, but are present
  265. // in the data source somehow, check them too.
  266. switch (result.code) {
  267. case result::SUCCESS:
  268. // If we found an exact match, we have no hope to getting
  269. // a better one. Stop right here.
  270. // TODO: In case we have only the datasource and not the finder
  271. // and the need_updater parameter is true, get the zone there.
  272. candidate.datasrc_client = client;
  273. candidate.finder = result.zone_finder;
  274. candidate.matched = true;
  275. candidate.exact = true;
  276. candidate.info = &info;
  277. return;
  278. case result::PARTIALMATCH:
  279. if (!want_exact_match) {
  280. // In case we have a partial match, check if it is better
  281. // than what we have. If so, replace it.
  282. //
  283. // We don't need the labels at the first partial match,
  284. // we have nothing to compare with. So we don't get it
  285. // (as a performance) and hope we will not need it at all.
  286. const uint8_t labels(candidate.matched ?
  287. result.zone_finder->getOrigin().getLabelCount() : 0);
  288. if (candidate.matched && candidate.matched_labels == 0) {
  289. // But if the hope turns out to be false, we need to
  290. // compute it for the first match anyway.
  291. candidate.matched_labels = candidate.finder->
  292. getOrigin().getLabelCount();
  293. }
  294. if (labels > candidate.matched_labels ||
  295. !candidate.matched) {
  296. // This one is strictly better. Replace it.
  297. candidate.datasrc_client = client;
  298. candidate.finder = result.zone_finder;
  299. candidate.matched_labels = labels;
  300. candidate.matched = true;
  301. candidate.info = &info;
  302. }
  303. }
  304. break;
  305. default:
  306. // Nothing found, nothing to do.
  307. break;
  308. }
  309. }
  310. // TODO: In case we have only the datasource and not the finder
  311. // and the need_updater parameter is true, get the zone there.
  312. }
  313. ConfigurableClientList::ReloadResult
  314. ConfigurableClientList::reload(const Name& name) {
  315. if (!allow_cache_) {
  316. return (CACHE_DISABLED);
  317. }
  318. // Try to find the correct zone.
  319. MutableResult result;
  320. findInternal(result, name, true, true);
  321. if (!result.finder) {
  322. return (ZONE_NOT_FOUND);
  323. }
  324. // Try to convert the finder to in-memory one. If it is the cache,
  325. // it should work.
  326. // It is of a different type or there's no cache.
  327. if (!result.info->cache_) {
  328. return (ZONE_NOT_CACHED);
  329. }
  330. DataSourceClient* client(result.info->data_src_client_);
  331. if (client) {
  332. // Now do the final reload. If it does not exist in client,
  333. // DataSourceError is thrown, which is exactly the result what we
  334. // want, so no need to handle it.
  335. ZoneIteratorPtr iterator(client->getIterator(name));
  336. if (!iterator) {
  337. isc_throw(isc::Unexpected, "Null iterator from " << name);
  338. }
  339. result.info->cache_->load(name, *iterator);
  340. } else {
  341. // The MasterFiles special case
  342. const string filename(result.info->cache_->getFileName(name));
  343. if (filename.empty()) {
  344. isc_throw(isc::Unexpected, "Confused about missing both filename "
  345. "and data source");
  346. }
  347. result.info->cache_->load(name, filename);
  348. }
  349. return (ZONE_RELOADED);
  350. }
  351. // NOTE: This function is not tested, it would be complicated. However, the
  352. // purpose of the function is to provide a very thin wrapper to be able to
  353. // replace the call to DataSourceClientContainer constructor in tests.
  354. ConfigurableClientList::DataSourcePair
  355. ConfigurableClientList::getDataSourceClient(const string& type,
  356. const ConstElementPtr&
  357. configuration)
  358. {
  359. DataSourceClientContainerPtr
  360. container(new DataSourceClientContainer(type, configuration));
  361. return (DataSourcePair(&container->getInstance(), container));
  362. }
  363. }
  364. }