client_list.cc 18 KB

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