client_list.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  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/memory_client.h"
  18. #include "memory/zone_table_segment.h"
  19. #include "memory/zone_writer.h"
  20. #include "memory/zone_data_loader.h"
  21. #include "logger.h"
  22. #include <dns/masterload.h>
  23. #include <util/memory_segment_local.h>
  24. #include <memory>
  25. #include <boost/foreach.hpp>
  26. #include <boost/bind.hpp>
  27. using namespace isc::data;
  28. using namespace isc::dns;
  29. using namespace std;
  30. using isc::util::MemorySegment;
  31. using boost::lexical_cast;
  32. using boost::shared_ptr;
  33. using boost::dynamic_pointer_cast;
  34. using isc::datasrc::memory::InMemoryClient;
  35. using isc::datasrc::memory::ZoneTableSegment;
  36. namespace isc {
  37. namespace datasrc {
  38. ConfigurableClientList::DataSourceInfo::DataSourceInfo(
  39. DataSourceClient* data_src_client,
  40. const DataSourceClientContainerPtr& container, bool has_cache,
  41. const RRClass& rrclass, const shared_ptr<ZoneTableSegment>& segment) :
  42. data_src_client_(data_src_client),
  43. container_(container)
  44. {
  45. if (has_cache) {
  46. cache_.reset(new InMemoryClient(segment, rrclass));
  47. ztable_segment_ = segment;
  48. }
  49. }
  50. ConfigurableClientList::DataSourceInfo::DataSourceInfo(
  51. const RRClass& rrclass, const shared_ptr<ZoneTableSegment>& segment,
  52. bool has_cache) :
  53. data_src_client_(NULL)
  54. {
  55. if (has_cache) {
  56. cache_.reset(new InMemoryClient(segment, rrclass));
  57. ztable_segment_ = segment;
  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. mem_sgmt_(new util::MemorySegmentLocal),
  67. configuration_(new isc::data::ListElement),
  68. allow_cache_(false)
  69. {}
  70. ConfigurableClientList::~ConfigurableClientList() {
  71. // Explicitly clear the contained data source clients, and check memory
  72. // leak. assert() (with abort on failure) may be too harsh, but
  73. // it's probably better to find more leaks initially. Once it's stabilized
  74. // we should probably revisit it.
  75. data_sources_.clear();
  76. assert(mem_sgmt_->allMemoryDeallocated());
  77. }
  78. void
  79. ConfigurableClientList::configure(const ConstElementPtr& config,
  80. bool allow_cache)
  81. {
  82. if (!config) {
  83. isc_throw(isc::BadValue, "NULL configuration passed");
  84. }
  85. // TODO: Implement recycling from the old configuration.
  86. size_t i(0); // Outside of the try to be able to access it in the catch
  87. try {
  88. vector<DataSourceInfo> new_data_sources;
  89. shared_ptr<ZoneTableSegment> ztable_segment(
  90. ZoneTableSegment::create(*config, rrclass_));
  91. for (; i < config->size(); ++i) {
  92. // Extract the parameters
  93. const ConstElementPtr dconf(config->get(i));
  94. const ConstElementPtr typeElem(dconf->get("type"));
  95. if (typeElem == ConstElementPtr()) {
  96. isc_throw(ConfigurationError, "Missing the type option in "
  97. "data source no " << i);
  98. }
  99. const string type(typeElem->stringValue());
  100. ConstElementPtr paramConf(dconf->get("params"));
  101. if (paramConf == ConstElementPtr()) {
  102. paramConf.reset(new NullElement());
  103. }
  104. const bool want_cache(allow_cache &&
  105. dconf->contains("cache-enable") &&
  106. dconf->get("cache-enable")->boolValue());
  107. if (type == "MasterFiles") {
  108. // In case the cache is not allowed, we just skip the master
  109. // files (at least for now)
  110. if (!allow_cache) {
  111. // We're not going to load these zones. Issue warnings about it.
  112. const map<string, ConstElementPtr>
  113. zones_files(paramConf->mapValue());
  114. for (map<string, ConstElementPtr>::const_iterator
  115. it(zones_files.begin()); it != zones_files.end();
  116. ++it) {
  117. LOG_WARN(logger, DATASRC_LIST_NOT_CACHED).
  118. arg(it->first).arg(rrclass_);
  119. }
  120. continue;
  121. }
  122. if (!want_cache) {
  123. isc_throw(ConfigurationError, "The cache must be enabled "
  124. "for the MasterFiles type");
  125. }
  126. new_data_sources.push_back(DataSourceInfo(rrclass_,
  127. ztable_segment,
  128. true));
  129. } else {
  130. // Ask the factory to create the data source for us
  131. const DataSourcePair ds(this->getDataSourceClient(type,
  132. paramConf));
  133. // And put it into the vector
  134. new_data_sources.push_back(DataSourceInfo(ds.first, ds.second,
  135. want_cache, rrclass_,
  136. ztable_segment));
  137. }
  138. if (want_cache) {
  139. if (!dconf->contains("cache-zones") && type != "MasterFiles") {
  140. isc_throw(isc::NotImplemented, "Auto-detection of zones "
  141. "to cache is not yet implemented, supply "
  142. "cache-zones parameter");
  143. // TODO: Auto-detect list of all zones in the
  144. // data source.
  145. }
  146. // List the zones we are loading
  147. vector<string> zones_origins;
  148. if (type == "MasterFiles") {
  149. const map<string, ConstElementPtr>
  150. zones_files(paramConf->mapValue());
  151. for (map<string, ConstElementPtr>::const_iterator
  152. it(zones_files.begin()); it != zones_files.end();
  153. ++it) {
  154. zones_origins.push_back(it->first);
  155. }
  156. } else {
  157. const ConstElementPtr zones(dconf->get("cache-zones"));
  158. for (size_t i(0); i < zones->size(); ++i) {
  159. zones_origins.push_back(zones->get(i)->stringValue());
  160. }
  161. }
  162. const shared_ptr<InMemoryClient>
  163. cache(new_data_sources.back().cache_);
  164. const DataSourceClient* const
  165. client(new_data_sources.back().data_src_client_);
  166. for (vector<string>::const_iterator it(zones_origins.begin());
  167. it != zones_origins.end(); ++it) {
  168. const Name origin(*it);
  169. if (type == "MasterFiles") {
  170. try {
  171. cache->load(origin,
  172. paramConf->get(*it)->stringValue());
  173. } catch (const isc::dns::MasterLoadError& mle) {
  174. LOG_ERROR(logger, DATASRC_MASTERLOAD_ERROR)
  175. .arg(mle.what());
  176. }
  177. } else {
  178. ZoneIteratorPtr iterator;
  179. try {
  180. iterator = client->getIterator(origin);
  181. } catch (const DataSourceError&) {
  182. isc_throw(ConfigurationError, "Unable to "
  183. "cache non-existent zone "
  184. << origin);
  185. }
  186. if (!iterator) {
  187. isc_throw(isc::Unexpected, "Got NULL iterator "
  188. "for zone " << origin);
  189. }
  190. cache->load(origin, *iterator);
  191. }
  192. }
  193. }
  194. }
  195. // If everything is OK up until now, we have the new configuration
  196. // ready. So just put it there and let the old one die when we exit
  197. // the scope.
  198. data_sources_.swap(new_data_sources);
  199. configuration_ = config;
  200. allow_cache_ = allow_cache;
  201. } catch (const TypeError& te) {
  202. isc_throw(ConfigurationError, "Malformed configuration at data source "
  203. "no. " << i << ": " << te.what());
  204. }
  205. }
  206. namespace {
  207. class CacheKeeper : public ClientList::FindResult::LifeKeeper {
  208. public:
  209. CacheKeeper(const boost::shared_ptr<InMemoryClient>& cache) :
  210. cache_(cache)
  211. {}
  212. private:
  213. const boost::shared_ptr<InMemoryClient> cache_;
  214. };
  215. class ContainerKeeper : public ClientList::FindResult::LifeKeeper {
  216. public:
  217. ContainerKeeper(const DataSourceClientContainerPtr& container) :
  218. container_(container)
  219. {}
  220. private:
  221. const DataSourceClientContainerPtr container_;
  222. };
  223. boost::shared_ptr<ClientList::FindResult::LifeKeeper>
  224. genKeeper(const ConfigurableClientList::DataSourceInfo* info) {
  225. if (info == NULL) {
  226. return (boost::shared_ptr<ClientList::FindResult::LifeKeeper>());
  227. }
  228. if (info->cache_) {
  229. return (boost::shared_ptr<ClientList::FindResult::LifeKeeper>(
  230. new CacheKeeper(info->cache_)));
  231. } else {
  232. return (boost::shared_ptr<ClientList::FindResult::LifeKeeper>(
  233. new ContainerKeeper(info->container_)));
  234. }
  235. }
  236. }
  237. // We have this class as a temporary storage, as the FindResult can't be
  238. // assigned.
  239. struct ConfigurableClientList::MutableResult {
  240. MutableResult() :
  241. datasrc_client(NULL),
  242. matched_labels(0),
  243. matched(false),
  244. exact(false),
  245. info(NULL)
  246. {}
  247. DataSourceClient* datasrc_client;
  248. ZoneFinderPtr finder;
  249. uint8_t matched_labels;
  250. bool matched;
  251. bool exact;
  252. const DataSourceInfo* info;
  253. operator FindResult() const {
  254. // Conversion to the right result.
  255. return (FindResult(datasrc_client, finder, exact, genKeeper(info)));
  256. }
  257. };
  258. ClientList::FindResult
  259. ConfigurableClientList::find(const dns::Name& name, bool want_exact_match,
  260. bool want_finder) const
  261. {
  262. MutableResult result;
  263. findInternal(result, name, want_exact_match, want_finder);
  264. return (result);
  265. }
  266. void
  267. ConfigurableClientList::findInternal(MutableResult& candidate,
  268. const dns::Name& name,
  269. bool want_exact_match, bool) const
  270. {
  271. BOOST_FOREACH(const DataSourceInfo& info, data_sources_) {
  272. DataSourceClient* client(info.cache_ ? info.cache_.get() :
  273. info.data_src_client_);
  274. const DataSourceClient::FindResult result(client->findZone(name));
  275. // TODO: Once we mark the zones that are not loaded, but are present
  276. // in the data source somehow, check them too.
  277. switch (result.code) {
  278. case result::SUCCESS:
  279. // If we found an exact match, we have no hope to getting
  280. // a better one. Stop right here.
  281. // TODO: In case we have only the datasource and not the finder
  282. // and the need_updater parameter is true, get the zone there.
  283. candidate.datasrc_client = client;
  284. candidate.finder = result.zone_finder;
  285. candidate.matched = true;
  286. candidate.exact = true;
  287. candidate.info = &info;
  288. return;
  289. case result::PARTIALMATCH:
  290. if (!want_exact_match) {
  291. // In case we have a partial match, check if it is better
  292. // than what we have. If so, replace it.
  293. //
  294. // We don't need the labels at the first partial match,
  295. // we have nothing to compare with. So we don't get it
  296. // (as a performance) and hope we will not need it at all.
  297. const uint8_t labels(candidate.matched ?
  298. result.zone_finder->getOrigin().getLabelCount() : 0);
  299. if (candidate.matched && candidate.matched_labels == 0) {
  300. // But if the hope turns out to be false, we need to
  301. // compute it for the first match anyway.
  302. candidate.matched_labels = candidate.finder->
  303. getOrigin().getLabelCount();
  304. }
  305. if (labels > candidate.matched_labels ||
  306. !candidate.matched) {
  307. // This one is strictly better. Replace it.
  308. candidate.datasrc_client = client;
  309. candidate.finder = result.zone_finder;
  310. candidate.matched_labels = labels;
  311. candidate.matched = true;
  312. candidate.info = &info;
  313. }
  314. }
  315. break;
  316. default:
  317. // Nothing found, nothing to do.
  318. break;
  319. }
  320. }
  321. // TODO: In case we have only the datasource and not the finder
  322. // and the need_updater parameter is true, get the zone there.
  323. }
  324. // We still provide this method for backward compatibility. But to not have
  325. // duplicate code, it is a thin wrapper around getCachedZoneWriter only.
  326. ConfigurableClientList::ReloadResult
  327. ConfigurableClientList::reload(const Name& name) {
  328. const ZoneWriterPair result(getCachedZoneWriter(name));
  329. if (result.first != ZONE_SUCCESS) {
  330. return (result.first);
  331. }
  332. assert(result.second);
  333. result.second->load();
  334. result.second->install();
  335. result.second->cleanup();
  336. return (ZONE_SUCCESS);
  337. }
  338. namespace {
  339. // We would like to use boost::bind for this. However, the loadZoneData takes
  340. // a reference, while we have a shared pointer to the iterator -- and we need
  341. // to keep it alive as long as the ZoneWriter is alive. Therefore we can't
  342. // really just dereference it and pass it, since it would get destroyed once
  343. // the getCachedZoneWriter would end. This class holds the shared pointer
  344. // alive, otherwise is mostly simple.
  345. //
  346. // It might be doable with nested boost::bind, but it would probably look
  347. // more awkward and complicated than this.
  348. class IteratorLoader {
  349. public:
  350. IteratorLoader(const RRClass& rrclass, const Name& name,
  351. const ZoneIteratorPtr& iterator) :
  352. rrclass_(rrclass),
  353. name_(name),
  354. iterator_(iterator)
  355. {}
  356. memory::ZoneData* operator()(util::MemorySegment& segment) {
  357. return (memory::loadZoneData(segment, rrclass_, name_, *iterator_));
  358. }
  359. private:
  360. const RRClass rrclass_;
  361. const Name name_;
  362. ZoneIteratorPtr iterator_;
  363. };
  364. // We can't use the loadZoneData function directly in boost::bind, since
  365. // it is overloaded and the compiler can't choose the correct version
  366. // reliably and fails. So we simply wrap it into an unique name.
  367. memory::ZoneData*
  368. loadZoneDataFromFile(util::MemorySegment& segment, const RRClass& rrclass,
  369. const Name& name, const string& filename)
  370. {
  371. return (memory::loadZoneData(segment, rrclass, name, filename));
  372. }
  373. }
  374. ConfigurableClientList::ZoneWriterPair
  375. ConfigurableClientList::getCachedZoneWriter(const Name& name) {
  376. if (!allow_cache_) {
  377. return (ZoneWriterPair(CACHE_DISABLED, ZoneWriterPtr()));
  378. }
  379. // Try to find the correct zone.
  380. MutableResult result;
  381. findInternal(result, name, true, true);
  382. if (!result.finder) {
  383. return (ZoneWriterPair(ZONE_NOT_FOUND, ZoneWriterPtr()));
  384. }
  385. // Try to get the in-memory cache for the zone. If there's none,
  386. // we can't provide the result.
  387. if (!result.info->cache_) {
  388. return (ZoneWriterPair(ZONE_NOT_CACHED, ZoneWriterPtr()));
  389. }
  390. memory::LoadAction load_action;
  391. DataSourceClient* client(result.info->data_src_client_);
  392. if (client != NULL) {
  393. // Now finally provide the writer.
  394. // If it does not exist in client,
  395. // DataSourceError is thrown, which is exactly the result what we
  396. // want, so no need to handle it.
  397. ZoneIteratorPtr iterator(client->getIterator(name));
  398. if (!iterator) {
  399. isc_throw(isc::Unexpected, "Null iterator from " << name);
  400. }
  401. // And wrap the iterator into the correct functor (which
  402. // keeps it alive as long as it is needed).
  403. load_action = IteratorLoader(rrclass_, name, iterator);
  404. } else {
  405. // The MasterFiles special case
  406. const string filename(result.info->cache_->getFileName(name));
  407. if (filename.empty()) {
  408. isc_throw(isc::Unexpected, "Confused about missing both filename "
  409. "and data source");
  410. }
  411. // boost::bind is enough here.
  412. load_action = boost::bind(loadZoneDataFromFile, _1, rrclass_, name,
  413. filename);
  414. }
  415. return (ZoneWriterPair(ZONE_SUCCESS,
  416. ZoneWriterPtr(
  417. result.info->ztable_segment_->
  418. getZoneWriter(load_action, name, rrclass_))));
  419. }
  420. // NOTE: This function is not tested, it would be complicated. However, the
  421. // purpose of the function is to provide a very thin wrapper to be able to
  422. // replace the call to DataSourceClientContainer constructor in tests.
  423. ConfigurableClientList::DataSourcePair
  424. ConfigurableClientList::getDataSourceClient(const string& type,
  425. const ConstElementPtr&
  426. configuration)
  427. {
  428. DataSourceClientContainerPtr
  429. container(new DataSourceClientContainer(type, configuration));
  430. return (DataSourcePair(&container->getInstance(), container));
  431. }
  432. }
  433. }