Commit d97165b6 authored by Mukund Sivaraman's avatar Mukund Sivaraman
Browse files

Merge branch 'trac2852_3'

Conflicts:
	src/lib/datasrc/client_list.h
parents dd34e731 ab78760e
......@@ -322,6 +322,21 @@ ConfigurableClientList::findInternal(MutableResult& candidate,
// and the need_updater parameter is true, get the zone there.
}
void
ConfigurableClientList::resetMemorySegment
(const std::string& datasrc_name,
ZoneTableSegment::MemorySegmentOpenMode mode,
ConstElementPtr config_params)
{
BOOST_FOREACH(DataSourceInfo& info, data_sources_) {
if (info.name_ == datasrc_name) {
ZoneTableSegment& segment = *info.ztable_segment_;
segment.reset(mode, config_params);
break;
}
}
}
ConfigurableClientList::ZoneWriterPair
ConfigurableClientList::getCachedZoneWriter(const Name& name,
const std::string& datasrc_name)
......
......@@ -21,7 +21,7 @@
#include <dns/rrclass.h>
#include <cc/data.h>
#include <exceptions/exceptions.h>
#include "memory/zone_table_segment.h"
#include <datasrc/memory/zone_table_segment.h>
#include <datasrc/zone_table_accessor.h>
#include <vector>
......@@ -364,6 +364,21 @@ public:
return (configuration_);
}
/// \brief Resets the zone table segment for a datasource with a new
/// memory segment.
///
/// See documentation of \c ZoneTableSegment interface
/// implementations (such as \c ZoneTableSegmentMapped) for the
/// syntax of \c config_params.
///
/// \param datasrc_name The name of the data source whose segment to reset
/// \param mode The open mode for the new memory segment
/// \param config_params The configuration for the new memory segment.
void resetMemorySegment
(const std::string& datasrc_name,
memory::ZoneTableSegment::MemorySegmentOpenMode mode,
isc::data::ConstElementPtr config_params);
private:
/// \brief Convenience type shortcut
typedef boost::shared_ptr<memory::ZoneWriter> ZoneWriterPtr;
......
......@@ -32,7 +32,9 @@
#include <gtest/gtest.h>
#include <boost/format.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/interprocess/file_mapping.hpp>
#include <set>
#include <fstream>
......@@ -105,7 +107,18 @@ const char* ds_zones[][3] = {
const size_t ds_count = (sizeof(ds_zones) / sizeof(*ds_zones));
class ListTest : public ::testing::Test {
class SegmentType {
public:
virtual ~SegmentType() {}
virtual ConstElementPtr getCacheConfig(bool enabled, const Name& zone)
const = 0;
virtual void reset(ConfigurableClientList& list,
const std::string& datasrc_name,
ZoneTableSegment::MemorySegmentOpenMode mode,
ConstElementPtr config_params) = 0;
};
class ListTest : public ::testing::TestWithParam<SegmentType*> {
public:
ListTest() :
rrclass_(RRClass::IN()),
......@@ -121,8 +134,7 @@ public:
" \"type\": \"test_type\","
" \"params\": [\"example.org\", \"example.com\", "
" \"noiter.org\", \"null.org\"]"
"}]")),
ztable_segment_(ZoneTableSegment::create(rrclass_, "local"))
"}]"))
{
for (size_t i(0); i < ds_count; ++ i) {
shared_ptr<MockDataSourceClient>
......@@ -135,6 +147,24 @@ public:
}
}
~ListTest() {
ds_info_.clear();
for (size_t i(0); i < ds_count; ++ i) {
ds_[i].reset();
}
ds_.clear();
for (size_t i(0); i < ds_count; ++ i) {
boost::interprocess::file_mapping::remove(
getMappedFilename(i).c_str());
}
}
static std::string getMappedFilename(size_t index) {
return (boost::str(boost::format(TEST_DATA_BUILDDIR "/test%d.mapped")
% index));
}
// Install a "fake" cached zone using a temporary underlying data source
// client. If 'enabled' is set to false, emulate a disabled cache, in
// which case there will be no data in memory.
......@@ -152,11 +182,7 @@ public:
// Build new cache config to load the specified zone, and replace
// the data source info with the new config.
ConstElementPtr cache_conf_elem =
Element::fromJSON("{\"type\": \"mock\","
" \"cache-enable\": " +
string(enabled ? "true," : "false,") +
" \"cache-zones\": "
" [\"" + zone.toText() + "\"]}");
GetParam()->getCacheConfig(enabled, zone);
boost::shared_ptr<internal::CacheConfig> cache_conf(
new internal::CacheConfig("mock", mock_client, *cache_conf_elem,
true));
......@@ -167,6 +193,15 @@ public:
// Load the data into the zone table.
if (enabled) {
const ConstElementPtr config_ztable_segment(
Element::fromJSON("{\"mapped-file\": \"" +
getMappedFilename(index) +
"\"}"));
GetParam()->reset(*list_, dsrc_info.name_,
memory::ZoneTableSegment::CREATE,
config_ztable_segment);
boost::scoped_ptr<memory::ZoneWriter> writer(
new memory::ZoneWriter(
*dsrc_info.ztable_segment_,
......@@ -175,6 +210,10 @@ public:
writer->load();
writer->install();
writer->cleanup(); // not absolutely necessary, but just in case
GetParam()->reset(*list_, dsrc_info.name_,
memory::ZoneTableSegment::READ_WRITE,
config_ztable_segment);
}
// On completion of load revert to the previous state of underlying
......@@ -275,11 +314,64 @@ public:
vector<shared_ptr<MockDataSourceClient> > ds_;
vector<ConfigurableClientList::DataSourceInfo> ds_info_;
const ConstElementPtr config_elem_, config_elem_zones_;
shared_ptr<ZoneTableSegment> ztable_segment_;
};
class LocalSegmentType : public SegmentType {
public:
virtual ConstElementPtr getCacheConfig(bool enabled, const Name& zone)
const
{
return (Element::fromJSON("{\"type\": \"mock\","
" \"cache-enable\": " +
string(enabled ? "true," : "false,") +
" \"cache-zones\": "
" [\"" + zone.toText() + "\"]}"));
}
virtual void reset(ConfigurableClientList&, const std::string&,
ZoneTableSegment::MemorySegmentOpenMode,
ConstElementPtr) {
// We must not call reset on local ZoneTableSegments.
}
};
LocalSegmentType local_segment_type;
INSTANTIATE_TEST_CASE_P(ListTestLocal, ListTest,
::testing::Values(static_cast<SegmentType*>(
&local_segment_type)));
#ifdef USE_SHARED_MEMORY
class MappedSegmentType : public SegmentType {
public:
virtual ConstElementPtr getCacheConfig(bool enabled, const Name& zone)
const
{
return (Element::fromJSON("{\"type\": \"mock\","
" \"cache-enable\": " +
string(enabled ? "true," : "false,") +
" \"cache-type\": \"mapped\"," +
" \"cache-zones\": "
" [\"" + zone.toText() + "\"]}"));
}
virtual void reset(ConfigurableClientList& list,
const std::string& datasrc_name,
ZoneTableSegment::MemorySegmentOpenMode mode,
ConstElementPtr config_params) {
list.resetMemorySegment(datasrc_name, mode, config_params);
}
};
MappedSegmentType mapped_segment_type;
INSTANTIATE_TEST_CASE_P(ListTestMapped, ListTest,
::testing::Values(static_cast<SegmentType*>(
&mapped_segment_type)));
#endif
// Test the test itself
TEST_F(ListTest, selfTest) {
TEST_P(ListTest, selfTest) {
EXPECT_EQ(result::SUCCESS, ds_[0]->findZone(Name("example.org")).code);
EXPECT_EQ(result::PARTIALMATCH,
ds_[0]->findZone(Name("sub.example.org")).code);
......@@ -293,14 +385,14 @@ TEST_F(ListTest, selfTest) {
}
// Test the list we create with empty configuration is, in fact, empty
TEST_F(ListTest, emptyList) {
TEST_P(ListTest, emptyList) {
EXPECT_TRUE(list_->getDataSources().empty());
}
// Check the values returned by a find on an empty list. It should be
// a negative answer (nothing found) no matter if we want an exact or inexact
// match.
TEST_F(ListTest, emptySearch) {
TEST_P(ListTest, emptySearch) {
// No matter what we try, we don't get an answer.
// Note: we don't have operator<< for the result class, so we cannot use
......@@ -317,7 +409,7 @@ TEST_F(ListTest, emptySearch) {
// Put a single data source inside the list and check it can find an
// exact match if there's one.
TEST_F(ListTest, singleDSExactMatch) {
TEST_P(ListTest, singleDSExactMatch) {
list_->getDataSources().push_back(ds_info_[0]);
// This zone is not there
EXPECT_TRUE(negative_result_ == list_->find(Name("org."), true));
......@@ -331,7 +423,7 @@ TEST_F(ListTest, singleDSExactMatch) {
}
// When asking for a partial match, we get all that the exact one, but more.
TEST_F(ListTest, singleDSBestMatch) {
TEST_P(ListTest, singleDSBestMatch) {
list_->getDataSources().push_back(ds_info_[0]);
// This zone is not there
EXPECT_TRUE(negative_result_ == list_->find(Name("org.")));
......@@ -351,7 +443,7 @@ const char* const test_names[] = {
"With a duplicity"
};
TEST_F(ListTest, multiExactMatch) {
TEST_P(ListTest, multiExactMatch) {
// Run through all the multi-configurations
for (size_t i(0); i < sizeof(test_names) / sizeof(*test_names); ++i) {
SCOPED_TRACE(test_names[i]);
......@@ -370,7 +462,7 @@ TEST_F(ListTest, multiExactMatch) {
}
}
TEST_F(ListTest, multiBestMatch) {
TEST_P(ListTest, multiBestMatch) {
// Run through all the multi-configurations
for (size_t i(0); i < 4; ++ i) {
SCOPED_TRACE(test_names[i]);
......@@ -391,7 +483,7 @@ TEST_F(ListTest, multiBestMatch) {
}
// Check the configuration is empty when the list is empty
TEST_F(ListTest, configureEmpty) {
TEST_P(ListTest, configureEmpty) {
const ConstElementPtr elem(new ListElement);
list_->configure(elem, true);
EXPECT_TRUE(list_->getDataSources().empty());
......@@ -400,7 +492,7 @@ TEST_F(ListTest, configureEmpty) {
}
// Check we can get multiple data sources and they are in the right order.
TEST_F(ListTest, configureMulti) {
TEST_P(ListTest, configureMulti) {
const ConstElementPtr elem(Element::fromJSON("["
"{"
" \"type\": \"type1\","
......@@ -422,7 +514,7 @@ TEST_F(ListTest, configureMulti) {
}
// Check we can pass whatever we want to the params
TEST_F(ListTest, configureParams) {
TEST_P(ListTest, configureParams) {
const char* params[] = {
"true",
"false",
......@@ -447,7 +539,7 @@ TEST_F(ListTest, configureParams) {
}
}
TEST_F(ListTest, status) {
TEST_P(ListTest, status) {
EXPECT_TRUE(list_->getStatus().empty());
const ConstElementPtr elem(Element::fromJSON("["
"{"
......@@ -474,7 +566,7 @@ TEST_F(ListTest, status) {
EXPECT_EQ("local", statuses[1].getSegmentType());
}
TEST_F(ListTest, wrongConfig) {
TEST_P(ListTest, wrongConfig) {
const char* configs[] = {
// A lot of stuff missing from there
"[{\"type\": \"test_type\", \"params\": 13}, {}]",
......@@ -572,7 +664,7 @@ TEST_F(ListTest, wrongConfig) {
}
// The param thing defaults to null. Cache is not used yet.
TEST_F(ListTest, defaults) {
TEST_P(ListTest, defaults) {
const ConstElementPtr elem(Element::fromJSON("["
"{"
" \"type\": \"type1\""
......@@ -583,7 +675,7 @@ TEST_F(ListTest, defaults) {
}
// Check we can call the configure multiple times, to change the configuration
TEST_F(ListTest, reconfigure) {
TEST_P(ListTest, reconfigure) {
const ConstElementPtr empty(new ListElement);
list_->configure(config_elem_, true);
checkDS(0, "test_type", "{}", false);
......@@ -594,7 +686,7 @@ TEST_F(ListTest, reconfigure) {
}
// Make sure the data source error exception from the factory is propagated
TEST_F(ListTest, dataSrcError) {
TEST_P(ListTest, dataSrcError) {
const ConstElementPtr elem(Element::fromJSON("["
"{"
" \"type\": \"error\""
......@@ -606,7 +698,7 @@ TEST_F(ListTest, dataSrcError) {
}
// Check we can get the cache
TEST_F(ListTest, configureCacheEmpty) {
TEST_P(ListTest, configureCacheEmpty) {
const ConstElementPtr elem(Element::fromJSON("["
"{"
" \"type\": \"type1\","
......@@ -628,7 +720,7 @@ TEST_F(ListTest, configureCacheEmpty) {
}
// But no cache if we disallow it globally
TEST_F(ListTest, configureCacheDisabled) {
TEST_P(ListTest, configureCacheDisabled) {
const ConstElementPtr elem(Element::fromJSON("["
"{"
" \"type\": \"type1\","
......@@ -650,7 +742,7 @@ TEST_F(ListTest, configureCacheDisabled) {
}
// Put some zones into the cache
TEST_F(ListTest, cacheZones) {
TEST_P(ListTest, cacheZones) {
const ConstElementPtr elem(Element::fromJSON("["
"{"
" \"type\": \"type1\","
......@@ -684,7 +776,7 @@ TEST_F(ListTest, cacheZones) {
// Check the caching handles misbehaviour from the data source and
// misconfiguration gracefully
TEST_F(ListTest, badCache) {
TEST_P(ListTest, badCache) {
list_->configure(config_elem_, true);
checkDS(0, "test_type", "{}", false);
// First, the zone is not in the data source. configure() should still
......@@ -733,7 +825,7 @@ TEST_F(ListTest, badCache) {
}
// This test relies on the property of mapped type of cache.
TEST_F(ListTest,
TEST_P(ListTest,
#ifdef USE_SHARED_MEMORY
cacheInNonWritableSegment
#else
......@@ -760,7 +852,7 @@ TEST_F(ListTest,
doReload(Name("example.org")));
}
TEST_F(ListTest, masterFiles) {
TEST_P(ListTest, masterFiles) {
const ConstElementPtr elem(Element::fromJSON("["
"{"
" \"type\": \"MasterFiles\","
......@@ -785,7 +877,7 @@ TEST_F(ListTest, masterFiles) {
}
// Test the names are set correctly and collission is detected.
TEST_F(ListTest, names) {
TEST_P(ListTest, names) {
// Explicit name
const ConstElementPtr elem1(Element::fromJSON("["
"{"
......@@ -832,7 +924,7 @@ TEST_F(ListTest, names) {
ConfigurableClientList::ConfigurationError);
}
TEST_F(ListTest, BadMasterFile) {
TEST_P(ListTest, BadMasterFile) {
// Configuration should succeed, and the good zones in the list
// below should be loaded. Bad zones won't be "loaded" in its usual sense,
// but are still recognized with conceptual "empty" data.
......@@ -908,7 +1000,7 @@ ListTest::doReload(const Name& origin, const string& datasrc_name) {
}
// Test we can reload a zone
TEST_F(ListTest, reloadSuccess) {
TEST_P(ListTest, reloadSuccess) {
list_->configure(config_elem_zones_, true);
const Name name("example.org");
prepareCache(0, name);
......@@ -928,7 +1020,7 @@ TEST_F(ListTest, reloadSuccess) {
}
// The cache is not enabled. The load should be rejected.
TEST_F(ListTest, reloadNotAllowed) {
TEST_P(ListTest, reloadNotAllowed) {
list_->configure(config_elem_zones_, false);
const Name name("example.org");
// We put the cache in even when not enabled. This won't confuse the thing.
......@@ -948,7 +1040,7 @@ TEST_F(ListTest, reloadNotAllowed) {
}
// Similar to the previous case, but the cache is disabled in config.
TEST_F(ListTest, reloadNotEnabled) {
TEST_P(ListTest, reloadNotEnabled) {
list_->configure(config_elem_zones_, true);
const Name name("example.org");
// We put the cache, actually disabling it.
......@@ -959,7 +1051,7 @@ TEST_F(ListTest, reloadNotEnabled) {
}
// Test several cases when the zone does not exist
TEST_F(ListTest, reloadNoSuchZone) {
TEST_P(ListTest, reloadNoSuchZone) {
list_->configure(config_elem_zones_, true);
const Name name("example.org");
// We put the cache in even when not enabled. This won't confuse the
......@@ -990,7 +1082,7 @@ TEST_F(ListTest, reloadNoSuchZone) {
// Check we gracefully reject reloading (i.e. no exception) when a zone
// disappeared in the underlying data source when we want to reload it
TEST_F(ListTest, reloadZoneGone) {
TEST_P(ListTest, reloadZoneGone) {
list_->configure(config_elem_zones_, true);
const Name name("example.org");
// We put in a cache for non-existent zone. This emulates being loaded
......@@ -1011,7 +1103,7 @@ TEST_F(ListTest, reloadZoneGone) {
list_->find(name).finder_->find(name, RRType::SOA())->code);
}
TEST_F(ListTest, reloadNewZone) {
TEST_P(ListTest, reloadNewZone) {
// Test the case where a zone to be cached originally doesn't exist
// in the underlying data source and is added later. reload() will
// succeed once it's available in the data source.
......@@ -1038,7 +1130,7 @@ TEST_F(ListTest, reloadNewZone) {
}
// The underlying data source throws. Check we don't modify the state.
TEST_F(ListTest, reloadZoneThrow) {
TEST_P(ListTest, reloadZoneThrow) {
list_->configure(config_elem_zones_, true);
const Name name("noiter.org");
prepareCache(0, name);
......@@ -1052,7 +1144,7 @@ TEST_F(ListTest, reloadZoneThrow) {
list_->find(name).finder_->find(name, RRType::SOA())->code);
}
TEST_F(ListTest, reloadNullIterator) {
TEST_P(ListTest, reloadNullIterator) {
list_->configure(config_elem_zones_, true);
const Name name("null.org");
prepareCache(0, name);
......@@ -1067,7 +1159,7 @@ TEST_F(ListTest, reloadNullIterator) {
}
// Test we can reload the master files too (special-cased)
TEST_F(ListTest, reloadMasterFile) {
TEST_P(ListTest, reloadMasterFile) {
const char* const install_cmd = INSTALL_PROG " -c " TEST_DATA_DIR
"/root.zone " TEST_DATA_BUILDDIR "/root.zone.copied";
if (system(install_cmd) != 0) {
......@@ -1102,7 +1194,7 @@ TEST_F(ListTest, reloadMasterFile) {
RRType::TXT())->code);
}
TEST_F(ListTest, reloadByDataSourceName) {
TEST_P(ListTest, reloadByDataSourceName) {
// We use three data sources (and their clients). 2nd and 3rd have
// the same name of the zones.
const ConstElementPtr config_elem = Element::fromJSON(
......
......@@ -27,6 +27,7 @@
#include <dns/python/rrclass_python.h>
#include <dns/python/name_python.h>
#include <dns/python/pydnspp_common.h>
#include <datasrc/client_list.h>
......@@ -38,7 +39,9 @@
using namespace std;
using namespace isc::util::python;
using namespace isc::datasrc;
using namespace isc::datasrc::memory;
using namespace isc::datasrc::python;
using namespace isc::dns::python;
//
// ConfigurableClientList
......@@ -115,6 +118,39 @@ ConfigurableClientList_configure(PyObject* po_self, PyObject* args) {
}
}
PyObject*
ConfigurableClientList_resetMemorySegment(PyObject* po_self, PyObject* args) {
s_ConfigurableClientList* self =
static_cast<s_ConfigurableClientList*>(po_self);
try {
const char* datasrc_name_p;
int mode_int;
const char* config_p;
if (PyArg_ParseTuple(args, "sis", &datasrc_name_p, &mode_int,
&config_p)) {
const std::string datasrc_name(datasrc_name_p);
const isc::data::ConstElementPtr
config(isc::data::Element::fromJSON(std::string(config_p)));
ZoneTableSegment::MemorySegmentOpenMode mode =
static_cast<ZoneTableSegment::MemorySegmentOpenMode>
(mode_int);
self->cppobj->resetMemorySegment(datasrc_name, mode, config);
Py_RETURN_NONE;
}
} catch (const isc::data::JSONError& jse) {
const string ex_what(std::string("JSON parse error in memory segment"
" configuration: ") + jse.what());
PyErr_SetString(getDataSourceException("Error"), ex_what.c_str());
} catch (const std::exception& exc) {
PyErr_SetString(getDataSourceException("Error"), exc.what());
} catch (...) {
PyErr_SetString(getDataSourceException("Error"),
"Unknown C++ exception");
}
return (NULL);
}
PyObject*
ConfigurableClientList_find(PyObject* po_self, PyObject* args) {
s_ConfigurableClientList* self =
......@@ -191,6 +227,19 @@ configuration preserved.\n\
Parameters:\n\
configuration The configuration, as a JSON encoded string.\
allow_cache If caching is allowed." },
{ "reset_memory_segment", ConfigurableClientList_resetMemorySegment,
METH_VARARGS,
"reset_memory_segment(datasrc_name, mode, config_params) -> None\n\
\n\
Wrapper around C++ ConfigurableClientList::resetMemorySegment\n\
\n\
This resets the zone table segment for a datasource with a new\n\
memory segment.\n\
\n\
Parameters:\n\
datasrc_name The name of the data source whose segment to reset.\
mode The open mode for the new memory segment.\
config_params The configuration for the new memory segment, as a JSON encoded string." },
{ "find", ConfigurableClientList_find, METH_VARARGS,
"find(zone, want_exact_match=False, want_finder=True) -> datasrc_client,\
zone_finder, exact_match\n\
......@@ -300,6 +349,29 @@ initModulePart_ConfigurableClientList(PyObject* mod) {
}
Py_INCREF(&configurableclientlist_type);
// FIXME: These should eventually be moved to the ZoneTableSegment
// class when we add Python bindings for the memory data source
// specific bits. But for now, we add these enums here to support
// reloading a zone table segment.
try {
installClassVariable(configurableclientlist_type, "CREATE",
Py_BuildValue("I", ZoneTableSegment::CREATE));
installClassVariable(configurableclientlist_type, "READ_WRITE",
Py_BuildValue("I", ZoneTableSegment::READ_WRITE));
installClassVariable(configurableclientlist_type, "READ_ONLY",
Py_BuildValue("I", ZoneTableSegment::READ_ONLY));
} catch (const std::exception& ex) {
const std::string ex_what =
"Unexpected failure in ConfigurableClientList initialization: " +
std::string(ex.what());
PyErr_SetString(po_IscException, ex_what.c_str());
return (false);
} catch (...) {
PyErr_SetString(PyExc_SystemError,
"Unexpected failure in ConfigurableClientList initialization");
return (false);
}
return (true);
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment