Commit bf60255c authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[master] Merge branch 'trac2834'

parents f85cdd32 2b67b050
......@@ -42,7 +42,7 @@
#include <asiodns/dns_service.h>
#include <datasrc/data_source.h>
#include <datasrc/exceptions.h>
#include <datasrc/client_list.h>
#include <xfr/xfrout_client.h>
......
......@@ -25,7 +25,7 @@
#include <cc/data.h>
#include <datasrc/data_source.h>
#include <datasrc/exceptions.h>
#include <datasrc/client_list.h>
#include <datasrc/memory/zone_writer.h>
......
......@@ -21,7 +21,7 @@
#include <cc/data.h>
#include <datasrc/data_source.h>
#include <datasrc/exceptions.h>
#include <xfr/xfrout_client.h>
......
......@@ -24,8 +24,7 @@ CLEANFILES += datasrc_config.h
CLEANFILES += static.zone
lib_LTLIBRARIES = libb10-datasrc.la
libb10_datasrc_la_SOURCES = data_source.h
libb10_datasrc_la_SOURCES += exceptions.h
libb10_datasrc_la_SOURCES = exceptions.h
libb10_datasrc_la_SOURCES += zone.h zone_finder.h zone_finder.cc
libb10_datasrc_la_SOURCES += zone_finder_context.cc
libb10_datasrc_la_SOURCES += zone_iterator.h
......
......@@ -15,10 +15,19 @@
#include <datasrc/cache_config.h>
#include <datasrc/client.h>
#include <datasrc/memory/load_action.h>
#include <datasrc/memory/zone_data_loader.h>
#include <util/memory_segment.h>
#include <dns/name.h>
#include <dns/rrclass.h>
#include <cc/data.h>
#include <exceptions/exceptions.h>
#include <boost/bind.hpp>
#include <cassert>
#include <map>
#include <string>
......@@ -108,6 +117,82 @@ CacheConfig::CacheConfig(const std::string& datasrc_type,
}
}
namespace {
// We would like to use boost::bind for this. However, the loadZoneData takes
// a reference, while we have a shared pointer to the iterator -- and we need
// to keep it alive as long as the ZoneWriter is alive. Therefore we can't
// really just dereference it and pass it, since it would get destroyed once
// the getCachedZoneWriter would end. This class holds the shared pointer
// alive, otherwise is mostly simple.
//
// It might be doable with nested boost::bind, but it would probably look
// more awkward and complicated than this.
class IteratorLoader {
public:
IteratorLoader(const dns::RRClass& rrclass, const dns::Name& name,
const ZoneIteratorPtr& iterator) :
rrclass_(rrclass),
name_(name),
iterator_(iterator)
{}
memory::ZoneData* operator()(util::MemorySegment& segment) {
return (memory::loadZoneData(segment, rrclass_, name_, *iterator_));
}
private:
const dns::RRClass rrclass_;
const dns::Name name_;
ZoneIteratorPtr iterator_;
};
// We can't use the loadZoneData function directly in boost::bind, since
// it is overloaded and the compiler can't choose the correct version
// reliably and fails. So we simply wrap it into an unique name.
memory::ZoneData*
loadZoneDataFromFile(util::MemorySegment& segment, const dns::RRClass& rrclass,
const dns::Name& name, const std::string& filename)
{
return (memory::loadZoneData(segment, rrclass, name, filename));
}
} // unnamed namespace
memory::LoadAction
CacheConfig::getLoadAction(const dns::RRClass& rrclass,
const dns::Name& zone_name) const
{
// First, check if the specified zone is configured to be cached.
Zones::const_iterator found = zone_config_.find(zone_name);
if (found == zone_config_.end()) {
return (memory::LoadAction());
}
if (!found->second.empty()) {
// This is "MasterFiles" data source.
return (boost::bind(loadZoneDataFromFile, _1, rrclass, zone_name,
found->second));
}
// Otherwise there must be a "source" data source (ensured by constructor)
assert(datasrc_client_);
// If the specified zone name does not exist in our client of the source,
// DataSourceError is thrown, which is exactly the result what we
// want, so no need to handle it.
ZoneIteratorPtr iterator(datasrc_client_->getIterator(zone_name));
if (!iterator) {
// This shouldn't happen for a compliant implementation of
// DataSourceClient, but we'll protect ourselves from buggy
// implementations.
isc_throw(Unexpected, "getting LoadAction for " << zone_name
<< "/" << rrclass << " resulted in Null zone iterator");
}
// Wrap the iterator into the correct functor (which keeps it alive as
// long as it is needed).
return (IteratorLoader(rrclass, zone_name, iterator));
}
} // namespace internal
} // namespace datasrc
} // namespace isc
......@@ -17,7 +17,7 @@
#include <exceptions/exceptions.h>
#include <dns/name.h>
#include <dns/dns_fwd.h>
#include <cc/data.h>
#include <datasrc/memory/load_action.h>
......@@ -53,7 +53,6 @@ public:
/// object that can be used for loading zones, regardless of the underlying
/// data source properties, i.e., whether it's special "MasterFiles" type
/// or other generic data sources.
/// NOTE: this part will be done in #2834.
///
/// This class is publicly defined so it can be tested directly, but
/// it's essentially private to the \c ConfigurableClientList class.
......@@ -144,12 +143,56 @@ public:
/// \throw None
const std::string& getSegmentType() const { return (segment_type_); }
/// \todo the following definition is tentative, mainly for tests.
/// In #2834 we'll (probably) extend it to be a custom iterator so
/// the caller can iterate over the whole set of zones, loading the
/// content in memory.
/// \brief Return a \c LoadAction functor to load zone data into memory.
///
/// This method returns an appropriate \c LoadAction functor that can be
/// passed to a \c memory::ZoneWriter object to load data of the specified
/// zone into memory. The source of the zone data differs depending on
/// the cache configuration (either a master file or another data source),
/// but this method hides the details and works as a unified interface
/// for the caller.
///
/// If the specified zone is not configured to be cached, it returns an
/// empty functor (which can be evaluated to be \c false as a boolean).
/// It doesn't throw an exception in this case because the expected caller
/// of this method would handle such a case internally.
///
/// \throw DataSourceError error happens in the underlying data source
/// storing the cache data. Most commonly it's because the specified zone
/// doesn't exist there.
/// \throw Unexpected Unexpected error happens in the underlying data
/// source storing the cache data. This shouldn't happen as long as the
/// data source implementation meets the public API requirement.
///
/// \param rrclass The RR class of the zone
/// \param zone_name The origin name of the zone
/// \return A \c LoadAction functor to load zone data or an empty functor
/// (see above).
memory::LoadAction getLoadAction(const dns::RRClass& rrlcass,
const dns::Name& zone_name) const;
/// \brief Read only iterator type over configured cached zones.
///
/// \note This initial version exposes the internal data structure (i.e.
/// map from name to string) through this public iterator type for
/// simplicity. In terms of data encapsulation it's better to introduce
/// a custom iterator type that only goes through the conceptual list
/// of zone names, but due to the limitation of the expected user of this
/// class that would probably be premature generalization. In future,
/// we might want to allow getting the list of zones directly from the
/// underlying data source. If and when that happens we should introduce
/// a custom type. In any case, the user of this class should only
/// use the typedef, not the original map iterator. It should also
/// use this iterator as a forward iterator (datasource-based iterator
/// wouldn't be able to be bidirectional), and it shouldn't use the
/// value of the map entry (a string, specifying a path to master file
/// for MasterFiles data source).
typedef std::map<dns::Name, std::string>::const_iterator ConstZoneIterator;
/// \brief Return the beginning of cached zones in the form of iterator.
ConstZoneIterator begin() const { return (zone_config_.begin()); }
/// \brief Return the end of cached zones in the form of iterator.
ConstZoneIterator end() const { return (zone_config_.end()); }
private:
......@@ -158,6 +201,9 @@ private:
// client of underlying data source, will be NULL for MasterFile datasrc
const DataSourceClient* datasrc_client_;
// Maps each of zones to be cached to a string. For "MasterFiles" type
// of data source, the string is a path to the master zone file; for
// others it's an empty string.
typedef std::map<dns::Name, std::string> Zones;
Zones zone_config_;
};
......
......@@ -31,6 +31,7 @@
#include <set>
#include <boost/foreach.hpp>
#include <boost/bind.hpp>
#include <boost/scoped_ptr.hpp>
using namespace isc::data;
using namespace isc::dns;
......@@ -119,6 +120,9 @@ ConfigurableClientList::configure(const ConstElementPtr& config,
continue;
}
// Build in-memory cache configuration, and create a set of
// related objects including the in-memory zone table for the
// cache.
boost::shared_ptr<internal::CacheConfig> cache_conf(
new internal::CacheConfig(type, dsrc_pair.first, *dconf,
allow_cache));
......@@ -127,60 +131,38 @@ ConfigurableClientList::configure(const ConstElementPtr& config,
cache_conf, rrclass_,
name));
if (cache_conf->isEnabled()) {
// List the zones we are loading
vector<string> zones_origins;
if (type == "MasterFiles") {
const map<string, ConstElementPtr>
zones_files(paramConf->mapValue());
for (map<string, ConstElementPtr>::const_iterator
it(zones_files.begin()); it != zones_files.end();
++it) {
zones_origins.push_back(it->first);
}
} else {
const ConstElementPtr zones(dconf->get("cache-zones"));
for (size_t i(0); i < zones->size(); ++i) {
zones_origins.push_back(zones->get(i)->stringValue());
}
// If cache is disabled we are done for this data source.
// Otherwise load zones into the in-memory cache.
if (!cache_conf->isEnabled()) {
continue;
}
internal::CacheConfig::ConstZoneIterator end_of_zones =
cache_conf->end();
for (internal::CacheConfig::ConstZoneIterator zone_it =
cache_conf->begin();
zone_it != end_of_zones;
++zone_it)
{
const Name& zname = zone_it->first;
memory::LoadAction load_action;
try {
load_action = cache_conf->getLoadAction(rrclass_, zname);
} catch (const DataSourceError&) {
isc_throw(ConfigurationError, "Data source error for "
"loading a zone (possibly non-existent) "
<< zname << "/" << rrclass_);
}
const shared_ptr<InMemoryClient>
cache(new_data_sources.back().cache_);
const DataSourceClient* const
client(new_data_sources.back().data_src_client_);
for (vector<string>::const_iterator it(zones_origins.begin());
it != zones_origins.end(); ++it) {
const Name origin(*it);
if (type == "MasterFiles") {
try {
cache->load(origin,
paramConf->get(*it)->stringValue());
} catch (const ZoneLoaderException& e) {
LOG_ERROR(logger, DATASRC_LOAD_FROM_FILE_ERROR)
.arg(origin).arg(e.what());
}
} else {
ZoneIteratorPtr iterator;
try {
iterator = client->getIterator(origin);
} catch (const DataSourceError&) {
isc_throw(ConfigurationError, "Unable to "
"cache non-existent zone "
<< origin);
}
if (!iterator) {
isc_throw(isc::Unexpected, "Got NULL iterator "
"for zone " << origin);
}
try {
cache->load(origin, *iterator);
} catch (const ZoneLoaderException& e) {
LOG_ERROR(logger, DATASRC_LOAD_FROM_ITERATOR_ERROR)
.arg(origin).arg(e.what());
}
}
assert(load_action); // in this loop this should be always true
boost::scoped_ptr<memory::ZoneWriter> writer;
try {
writer.reset(new_data_sources.back().ztable_segment_->
getZoneWriter(load_action, zname, rrclass_));
writer->load();
writer->install();
writer->cleanup();
} catch (const ZoneLoaderException& e) {
LOG_ERROR(logger, DATASRC_LOAD_ZONE_ERROR)
.arg(zname).arg(rrclass_).arg(name).arg(e.what());
}
}
}
......@@ -344,46 +326,6 @@ ConfigurableClientList::reload(const Name& name) {
return (ZONE_SUCCESS);
}
namespace {
// We would like to use boost::bind for this. However, the loadZoneData takes
// a reference, while we have a shared pointer to the iterator -- and we need
// to keep it alive as long as the ZoneWriter is alive. Therefore we can't
// really just dereference it and pass it, since it would get destroyed once
// the getCachedZoneWriter would end. This class holds the shared pointer
// alive, otherwise is mostly simple.
//
// It might be doable with nested boost::bind, but it would probably look
// more awkward and complicated than this.
class IteratorLoader {
public:
IteratorLoader(const RRClass& rrclass, const Name& name,
const ZoneIteratorPtr& iterator) :
rrclass_(rrclass),
name_(name),
iterator_(iterator)
{}
memory::ZoneData* operator()(util::MemorySegment& segment) {
return (memory::loadZoneData(segment, rrclass_, name_, *iterator_));
}
private:
const RRClass rrclass_;
const Name name_;
ZoneIteratorPtr iterator_;
};
// We can't use the loadZoneData function directly in boost::bind, since
// it is overloaded and the compiler can't choose the correct version
// reliably and fails. So we simply wrap it into an unique name.
memory::ZoneData*
loadZoneDataFromFile(util::MemorySegment& segment, const RRClass& rrclass,
const Name& name, const string& filename)
{
return (memory::loadZoneData(segment, rrclass, name, filename));
}
}
ConfigurableClientList::ZoneWriterPair
ConfigurableClientList::getCachedZoneWriter(const Name& name) {
if (!allow_cache_) {
......@@ -395,36 +337,15 @@ ConfigurableClientList::getCachedZoneWriter(const Name& name) {
if (!result.finder) {
return (ZoneWriterPair(ZONE_NOT_FOUND, ZoneWriterPtr()));
}
// Try to get the in-memory cache for the zone. If there's none,
// we can't provide the result.
if (!result.info->cache_) {
// Then get the appropriate load action and create a zone writer.
// Note that getCacheConfig() must return non NULL in this module (only
// tests could set it to a bogus value).
const memory::LoadAction load_action =
result.info->getCacheConfig()->getLoadAction(rrclass_, name);
if (!load_action) {
return (ZoneWriterPair(ZONE_NOT_CACHED, ZoneWriterPtr()));
}
memory::LoadAction load_action;
DataSourceClient* client(result.info->data_src_client_);
if (client != NULL) {
// Now finally provide the writer.
// If it does not exist in client,
// DataSourceError is thrown, which is exactly the result what we
// want, so no need to handle it.
ZoneIteratorPtr iterator(client->getIterator(name));
if (!iterator) {
isc_throw(isc::Unexpected, "Null iterator from " << name);
}
// And wrap the iterator into the correct functor (which
// keeps it alive as long as it is needed).
load_action = IteratorLoader(rrclass_, name, iterator);
} else {
// The MasterFiles special case
const string filename(result.info->cache_->getFileName(name));
if (filename.empty()) {
isc_throw(isc::Unexpected, "Confused about missing both filename "
"and data source");
}
// boost::bind is enough here.
load_action = boost::bind(loadZoneDataFromFile, _1, rrclass_, name,
filename);
}
return (ZoneWriterPair(ZONE_SUCCESS,
ZoneWriterPtr(
result.info->ztable_segment_->
......
......@@ -342,8 +342,10 @@ public:
/// \brief Result of the reload() method.
enum ReloadResult {
CACHE_DISABLED, ///< The cache is not enabled in this list.
ZONE_NOT_CACHED, ///< Zone is served directly, not from cache.
ZONE_NOT_FOUND, ///< Zone does not exist or not cached.
ZONE_NOT_CACHED, ///< Zone is served directly, not from cache
/// (including the case cache is disabled for
/// the specific data source).
ZONE_NOT_FOUND, ///< Zone does not exist in this list.
ZONE_SUCCESS ///< The zone was successfully reloaded or
/// the writer provided.
};
......@@ -418,6 +420,10 @@ public:
boost::shared_ptr<memory::InMemoryClient> cache_;
boost::shared_ptr<memory::ZoneTableSegment> ztable_segment_;
std::string name_;
const internal::CacheConfig* getCacheConfig() const {
return (cache_conf_.get());
}
private:
// this is kept private for now. When it needs to be accessed,
// we'll add a read-only getter method.
......
......@@ -17,7 +17,7 @@
#include <vector>
#include <datasrc/database.h>
#include <datasrc/data_source.h>
#include <datasrc/exceptions.h>
#include <datasrc/zone_iterator.h>
#include <datasrc/rrset_collection_base.h>
......@@ -30,7 +30,7 @@
#include <dns/rdataclass.h>
#include <dns/nsec3hash.h>
#include <datasrc/data_source.h>
#include <datasrc/exceptions.h>
#include <datasrc/logger.h>
#include <boost/foreach.hpp>
......
......@@ -24,7 +24,7 @@
#include <dns/rrset.h>
#include <dns/rrtype.h>
#include <datasrc/data_source.h>
#include <datasrc/exceptions.h>
#include <datasrc/client.h>
#include <datasrc/zone.h>
#include <datasrc/logger.h>
......
......@@ -354,15 +354,12 @@ Therefore, the entire data source will not be available for this process. If
this is a problem, you should configure the zones of that data source to some
database backend (sqlite3, for example) and use it from there.
% DATASRC_LOAD_FROM_FILE_ERROR Error loading zone %1: %2
An error was found in the zone data when it was being loaded from a
file. The zone was not loaded. The specific error is shown in the
message, and should be addressed.
% DATASRC_LOAD_FROM_ITERATOR_ERROR Error loading zone %1: %2
An error was found in the zone data when it was being loaded from
another data source. The zone was not loaded. The specific error is
shown in the message, and should be addressed.
% DATASRC_LOAD_ZONE_ERROR Error loading zone %1/%2 on data source %3: %4
During data source configuration, an error was found in the zone data
when it was being loaded in to memory on the shown data source. This
particular zone was not loaded, but data source configuration
continues, possibly loading other zones into memory. The specific
error is shown in the message, and should be addressed.
% DATASRC_MASTER_LOAD_ERROR %1:%2: Zone '%3/%4' contains error: %5
There's an error in the given master file. The zone won't be loaded for
......
......@@ -20,6 +20,26 @@
namespace isc {
namespace datasrc {
/// This exception represents Backend-independent errors relating to
/// data source operations.
class DataSourceError : public Exception {
public:
DataSourceError(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) {}
};
/// \brief No such serial number when obtaining difference iterator
///
/// Thrown if either the zone/start serial number or zone/end serial number
/// combination does not exist in the differences table. (Note that this
/// includes the case where the differences table contains no records related
/// to that zone.)
class NoSuchSerial : public DataSourceError {
public:
NoSuchSerial(const char* file, size_t line, const char* what) :
DataSourceError(file, line, what) {}
};
/// Base class for a number of exceptions that are thrown while working
/// with zones.
struct ZoneException : public Exception {
......
......@@ -14,7 +14,7 @@
#include "factory.h"
#include "data_source.h"
#include "exceptions.h"
#include "database.h"
#include "sqlite3_accessor.h"
......
......@@ -15,7 +15,7 @@
#ifndef DATA_SOURCE_FACTORY_H
#define DATA_SOURCE_FACTORY_H 1
#include <datasrc/data_source.h>
#include <datasrc/exceptions.h>
#include <datasrc/client.h>
#include <cc/data.h>
......
......@@ -18,15 +18,11 @@
#include <datasrc/memory/logger.h>
#include <datasrc/memory/zone_data.h>
#include <datasrc/memory/rdataset.h>
#include <datasrc/memory/segment_object_holder.h>
#include <datasrc/memory/treenode_rrset.h>
#include <datasrc/memory/zone_finder.h>
#include <datasrc/memory/zone_data_loader.h>
#include <datasrc/memory/zone_table_segment.h>
#include <util/memory_segment_local.h>
#include <datasrc/data_source.h>
#include <datasrc/exceptions.h>
#include <datasrc/factory.h>
#include <datasrc/result.h>
......@@ -34,12 +30,8 @@
#include <dns/rdataclass.h>
#include <dns/rrclass.h>
#include <algorithm>
#include <utility>
#include <cctype>
#include <cassert>
using namespace std;
using namespace isc::dns;
using namespace isc::dns::rdata;
using namespace isc::datasrc::memory;
......@@ -49,86 +41,14 @@ namespace isc {
namespace datasrc {
namespace memory {
using detail::SegmentObjectHolder;
using boost::shared_ptr;
namespace { // unnamed namespace
// A helper internal class used by the memory client, used for deleting
// filenames stored in an internal tree.
class FileNameDeleter {
public:
FileNameDeleter() {}
void operator()(std::string* filename) const {
delete filename;
}
};
} // end of unnamed namespace
InMemoryClient::InMemoryClient(shared_ptr<ZoneTableSegment> ztable_segment,
RRClass rrclass) :
ztable_segment_(ztable_segment),
rrclass_(rrclass),
zone_count_(0),
file_name_tree_(FileNameTree::create(
ztable_segment_->getMemorySegment(), false))
rrclass_(rrclass)
{}
InMemoryClient::~InMemoryClient() {
MemorySegment& mem_sgmt = ztable_segment_->getMemorySegment();
FileNameDeleter deleter;