client_list.cc 19 KB

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