BIND 10 master, updated. ed713618c11b1e63b4a6e10fb487a4bf3339c2ac Merge #2862
BIND 10 source code commits
bind10-changes at lists.isc.org
Mon Jul 29 08:43:01 UTC 2013
The branch, master has been updated
via ed713618c11b1e63b4a6e10fb487a4bf3339c2ac (commit)
via 9694e88402b35b3df56880c656270c1108e0df13 (commit)
via 478d2eda7aca5a6e7f4a0e017192854f100641b5 (commit)
via d8183b83d92016696106a6665fde369fa7d49779 (commit)
via 9cccfbbc4e13274cc1c5750704f03fbc577189ef (commit)
via f1c0d7070d9a3968ff900663bffcba20e53851b0 (commit)
via feebb25811b38ebfee97c2a11a1470f7aaaf6696 (commit)
via b05bfd741c5b4ec90ada47017af8a299f8a8b4d6 (commit)
via 0d8bee5c0cc3cf65a0a14890d7ff76de328e4316 (commit)
via 2aa3e8e3ad750691dc97badcac032ef1ca92737f (commit)
via 661dae35e38a4b4a7d3f22b785509ceb528003e1 (commit)
via 7276a2eddfc2fefb354a4848aa4376b9dbeb675e (commit)
via 3194a2019cc116c9c668cf4adbf879e06b927879 (commit)
via 7cd60567cc43fe83a74a7799373e5e6410d435f3 (commit)
via d6c9e292ff83ad12b82b8aa73e59ae05d23ee65a (commit)
via 617aa41e35a33420f00dae8bb9f39918208dc1a9 (commit)
via cb814de47219dbc310f277838106d5b4d7e17668 (commit)
via 228807659e177e95f4a3dbedc6978dcb8a02a2c3 (commit)
via ef523ea6235d16e0676d0263c964570134a59750 (commit)
via eb01e3dce1476254ec6720798c861e709d32c9be (commit)
via 87c3781cf964f63b044153a39b05518f72d8b80e (commit)
via 02e0859b64a98789b422bc57c457825bd991749f (commit)
via d26eac8dfc0780bd6211ff90f9d922bbe2bf7edb (commit)
from 3b2674982d558f22f9a2db3b43c922e29ba7520c (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit ed713618c11b1e63b4a6e10fb487a4bf3339c2ac
Merge: 3b26749 9694e88
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Mon Jul 29 09:26:46 2013 +0200
Merge #2862
Auth now recognizes the mapped segments and maps them.
commit 9694e88402b35b3df56880c656270c1108e0df13
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Mon Jul 29 09:22:51 2013 +0200
[2862] Document aborting on mapping error
-----------------------------------------------------------------------
Summary of changes:
doc/design/datasrc/data-source-classes.txt | 6 +-
src/bin/auth/auth_messages.mes | 24 +++++
src/bin/auth/auth_srv.cc | 64 ++++++++++++
src/bin/auth/auth_srv.h | 11 +++
src/bin/auth/datasrc_clients_mgr.h | 92 +++++++++++++++++-
src/bin/auth/main.cc | 6 +-
src/bin/auth/tests/auth_srv_unittest.cc | 46 ++++++++-
.../auth/tests/datasrc_clients_builder_unittest.cc | 102 ++++++++++++++++++++
src/bin/auth/tests/datasrc_clients_mgr_unittest.cc | 27 ++++++
src/lib/config/ccsession.cc | 2 +
src/lib/config/ccsession.h | 46 +++++++++
src/lib/config/tests/ccsession_unittests.cc | 37 +++++++
src/lib/datasrc/client_list.cc | 5 +-
src/lib/datasrc/client_list.h | 7 +-
src/lib/datasrc/tests/client_list_unittest.cc | 10 +-
15 files changed, 471 insertions(+), 14 deletions(-)
-----------------------------------------------------------------------
diff --git a/doc/design/datasrc/data-source-classes.txt b/doc/design/datasrc/data-source-classes.txt
index ac7e5a9..0f2dcbb 100644
--- a/doc/design/datasrc/data-source-classes.txt
+++ b/doc/design/datasrc/data-source-classes.txt
@@ -236,7 +236,8 @@ image::auth-mapped.png[Sequence diagram for auth server using mapped memory segm
argument and the segment mode of `READ_ONLY`.
Note that the auth module handles the command argument as mostly
opaque data; it's not expected to deal with details of segment
- type-specific behavior.
+ type-specific behavior. If the reset fails, auth aborts (as there's
+ no clear way to handle the failure).
6. `ConfigurableClientList::resetMemorySegment()` subsequently calls
`reset()` method on the corresponding `ZoneTableSegment` with the
@@ -254,7 +255,8 @@ image::auth-mapped.png[Sequence diagram for auth server using mapped memory segm
underlying memory segment is swapped with a new one. The old
memory segment object is destroyed. Note that
this "destroy" just means unmapping the memory region; the data
- stored in the file are intact.
+ stored in the file are intact. Again, if mapping fails, auth
+ aborts.
8. If the auth module happens to receive a reload command from other
module, it could call
diff --git a/src/bin/auth/auth_messages.mes b/src/bin/auth/auth_messages.mes
index e38468a..e5b655a 100644
--- a/src/bin/auth/auth_messages.mes
+++ b/src/bin/auth/auth_messages.mes
@@ -145,6 +145,30 @@ reconfigure, and has now started this process.
The thread for maintaining data source clients has finished reconfiguring
the data source clients, and is now running with the new configuration.
+% AUTH_DATASRC_CLIENTS_BUILDER_SEGMENT_BAD_CLASS invalid RRclass %1 at segment update
+A memory segment update message was sent to the authoritative
+server. But the class contained there is invalid. This means that the
+system is in an inconsistent state and the authoritative server aborts
+to minimize the problem. This is likely caused by a bug in the code.
+
+% AUTH_DATASRC_CLIENTS_BUILDER_SEGMENT_ERROR error updating the memory segment: %1
+The authoritative server tried to update the memory segment, but the update
+failed. The authoritative server aborts to avoid system inconsistency. This is
+likely caused by a bug in the code.
+
+% AUTH_DATASRC_CLIENTS_BUILDER_SEGMENT_NO_DATASRC there's no data source named %2 in class %1
+The authoritative server was asked to update the memory segment of the
+given data source, but no data source by that name was found. The
+authoritative server aborts because this indicates that the system is in
+an inconsistent state. This is likely caused by a bug in the code.
+
+% AUTH_DATASRC_CLIENTS_BUILDER_SEGMENT_UNKNOWN_CLASS unknown class %1 at segment update
+A memory segment update message was sent to the authoritative
+server. The class name for which the update should happen is valid, but
+no client lists are configured for that class. The system is in an
+inconsistent state and the authoritative server aborts. This may be
+caused by a bug in the code.
+
% AUTH_DATASRC_CLIENTS_BUILDER_STARTED data source builder thread started
A separate thread for maintaining data source clients has been started.
diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc
index 27779a9..bc160fd 100644
--- a/src/bin/auth/auth_srv.cc
+++ b/src/bin/auth/auth_srv.cc
@@ -306,6 +306,8 @@ public:
MessageAttributes& stats_attrs,
const bool done);
+ /// Are we currently subscribed to the SegmentReader group?
+ bool readers_group_subscribed_;
private:
bool xfrout_connected_;
AbstractXfroutClient& xfrout_client_;
@@ -322,6 +324,7 @@ AuthSrvImpl::AuthSrvImpl(AbstractXfroutClient& xfrout_client,
datasrc_clients_mgr_(io_service_),
ddns_base_forwarder_(ddns_forwarder),
ddns_forwarder_(NULL),
+ readers_group_subscribed_(false),
xfrout_connected_(false),
xfrout_client_(xfrout_client)
{}
@@ -939,3 +942,64 @@ void
AuthSrv::setTCPRecvTimeout(size_t timeout) {
dnss_->setTCPRecvTimeout(timeout);
}
+
+namespace {
+
+bool
+hasMappedSegment(auth::DataSrcClientsMgr& mgr) {
+ auth::DataSrcClientsMgr::Holder holder(mgr);
+ const std::vector<dns::RRClass>& classes(holder.getClasses());
+ BOOST_FOREACH(const dns::RRClass& rrclass, classes) {
+ const boost::shared_ptr<datasrc::ConfigurableClientList>&
+ list(holder.findClientList(rrclass));
+ const std::vector<DataSourceStatus>& states(list->getStatus());
+ BOOST_FOREACH(const datasrc::DataSourceStatus& status, states) {
+ if (status.getSegmentState() != datasrc::SEGMENT_UNUSED &&
+ status.getSegmentType() == "mapped")
+ // We use some segment and it's not a local one, so it
+ // must be remote.
+ return true;
+ }
+ }
+ // No remote segment found in any of the lists
+ return false;
+}
+
+}
+
+void
+AuthSrv::listsReconfigured() {
+ const bool has_remote = hasMappedSegment(impl_->datasrc_clients_mgr_);
+ if (has_remote && !impl_->readers_group_subscribed_) {
+ impl_->config_session_->subscribe("SegmentReader");
+ impl_->config_session_->
+ setUnhandledCallback(boost::bind(&AuthSrv::foreignCommand, this,
+ _1, _2, _3));
+ impl_->readers_group_subscribed_ = true;
+ } else if (!has_remote && impl_->readers_group_subscribed_) {
+ impl_->config_session_->unsubscribe("SegmentReader");
+ impl_->config_session_->
+ setUnhandledCallback(isc::config::ModuleCCSession::
+ UnhandledCallback());
+ impl_->readers_group_subscribed_ = false;
+ }
+}
+
+void
+AuthSrv::reconfigureDone(ConstElementPtr params) {
+ // ACK the segment
+ impl_->config_session_->
+ groupSendMsg(isc::config::createCommand("segment_info_update_ack",
+ params), "MemMgr");
+}
+
+void
+AuthSrv::foreignCommand(const std::string& command, const std::string&,
+ const ConstElementPtr& params)
+{
+ if (command == "segment_info_update") {
+ impl_->datasrc_clients_mgr_.
+ segmentInfoUpdate(params, boost::bind(&AuthSrv::reconfigureDone,
+ this, params));
+ }
+}
diff --git a/src/bin/auth/auth_srv.h b/src/bin/auth/auth_srv.h
index 8ad72be..1c49d4f 100644
--- a/src/bin/auth/auth_srv.h
+++ b/src/bin/auth/auth_srv.h
@@ -273,7 +273,18 @@ public:
/// open forever.
void setTCPRecvTimeout(size_t timeout);
+ /// \brief Notify the authoritative server that the client lists were
+ /// reconfigured.
+ ///
+ /// This is to be called when the work thread finishes reconfiguration
+ /// of the data sources. It involeves some book keeping and asking the
+ /// memory manager for segments, if some are remotely mapped.
+ void listsReconfigured();
+
private:
+ void reconfigureDone(isc::data::ConstElementPtr request);
+ void foreignCommand(const std::string& command, const std::string&,
+ const isc::data::ConstElementPtr& params);
AuthSrvImpl* impl_;
isc::asiolink::SimpleCallback* checkin_;
isc::asiodns::DNSLookup* dns_lookup_;
diff --git a/src/bin/auth/datasrc_clients_mgr.h b/src/bin/auth/datasrc_clients_mgr.h
index c84b9c0..c6eed31 100644
--- a/src/bin/auth/datasrc_clients_mgr.h
+++ b/src/bin/auth/datasrc_clients_mgr.h
@@ -81,6 +81,7 @@ enum CommandID {
LOADZONE, ///< Load a new version of zone into a memory,
/// the argument to the command is a map containing 'class'
/// and 'origin' elements, both should have been validated.
+ SEGMENT_INFO_UPDATE, ///< The memory manager sent an update about segments.
SHUTDOWN, ///< Shutdown the builder; no argument
NUM_COMMANDS
};
@@ -212,6 +213,24 @@ public:
return (it->second);
}
}
+ /// \brief Return list of classes that are present.
+ ///
+ /// Get the list of classes for which there's a client list. It is
+ /// returned in form of a vector, copied from the internals. As the
+ /// number of classes in there is expected to be small, it is not
+ /// a performance issue.
+ ///
+ /// \return The list of classes.
+ /// \throw std::bad_alloc for problems allocating the result.
+ std::vector<dns::RRClass> getClasses() const {
+ std::vector<dns::RRClass> result;
+ for (ClientListsMap::const_iterator it =
+ mgr_.clients_map_->begin(); it != mgr_.clients_map_->end();
+ ++it) {
+ result.push_back(it->first);
+ }
+ return (result);
+ }
private:
DataSrcClientsMgrBase& mgr_;
typename MutexType::Locker locker_;
@@ -381,6 +400,36 @@ public:
sendCommand(datasrc_clientmgr_internal::LOADZONE, args, callback);
}
+ void segmentInfoUpdate(const data::ConstElementPtr& args,
+ const datasrc_clientmgr_internal::FinishedCallback&
+ callback =
+ datasrc_clientmgr_internal::FinishedCallback()) {
+ // Some minimal validation
+ if (!args) {
+ isc_throw(CommandError, "segmentInfoUpdate argument empty");
+ }
+ if (args->getType() != isc::data::Element::map) {
+ isc_throw(CommandError, "segmentInfoUpdate argument not a map");
+ }
+ const char* params[] = {
+ "data-source-name",
+ "data-source-class",
+ "segment-params",
+ NULL
+ };
+ for (const char** param = params; *param; ++param) {
+ if (!args->contains(*param)) {
+ isc_throw(CommandError,
+ "segmentInfoUpdate argument has no '" << param <<
+ "' value");
+ }
+ }
+
+
+ sendCommand(datasrc_clientmgr_internal::SEGMENT_INFO_UPDATE, args,
+ callback);
+ }
+
private:
// This is expected to be called at the end of the destructor. It
// actually does nothing, but provides a customization point for
@@ -577,6 +626,44 @@ private:
}
}
+ void doSegmentUpdate(const isc::data::ConstElementPtr& arg) {
+ try {
+ const isc::dns::RRClass
+ rrclass(arg->get("data-source-class")->stringValue());
+ const std::string&
+ name(arg->get("data-source-name")->stringValue());
+ const isc::data::ConstElementPtr& segment_params =
+ arg->get("segment-params");
+ typename MutexType::Locker locker(*map_mutex_);
+ const boost::shared_ptr<isc::datasrc::ConfigurableClientList>&
+ list = (**clients_map_)[rrclass];
+ if (!list) {
+ LOG_FATAL(auth_logger,
+ AUTH_DATASRC_CLIENTS_BUILDER_SEGMENT_UNKNOWN_CLASS)
+ .arg(rrclass);
+ std::terminate();
+ }
+ if (!list->resetMemorySegment(name,
+ isc::datasrc::memory::ZoneTableSegment::READ_ONLY,
+ segment_params)) {
+ LOG_FATAL(auth_logger,
+ AUTH_DATASRC_CLIENTS_BUILDER_SEGMENT_NO_DATASRC)
+ .arg(rrclass).arg(name);
+ std::terminate();
+ }
+ } catch (const isc::dns::InvalidRRClass& irce) {
+ LOG_FATAL(auth_logger,
+ AUTH_DATASRC_CLIENTS_BUILDER_SEGMENT_BAD_CLASS)
+ .arg(arg->get("data-source-class"));
+ std::terminate();
+ } catch (const isc::Exception& e) {
+ LOG_FATAL(auth_logger,
+ AUTH_DATASRC_CLIENTS_BUILDER_SEGMENT_ERROR)
+ .arg(e.what());
+ std::terminate();
+ }
+ }
+
void doLoadZone(const isc::data::ConstElementPtr& arg);
boost::shared_ptr<datasrc::memory::ZoneWriter> getZoneWriter(
datasrc::ConfigurableClientList& client_list,
@@ -676,7 +763,7 @@ DataSrcClientsBuilderBase<MutexType, CondVarType>::handleCommand(
}
const boost::array<const char*, NUM_COMMANDS> command_desc = {
- {"NOOP", "RECONFIGURE", "LOADZONE", "SHUTDOWN"}
+ {"NOOP", "RECONFIGURE", "LOADZONE", "SEGMENT_INFO_UPDATE", "SHUTDOWN"}
};
LOG_DEBUG(auth_logger, DBGLVL_TRACE_BASIC,
AUTH_DATASRC_CLIENTS_BUILDER_COMMAND).arg(command_desc.at(cid));
@@ -687,6 +774,9 @@ DataSrcClientsBuilderBase<MutexType, CondVarType>::handleCommand(
case LOADZONE:
doLoadZone(command.params);
break;
+ case SEGMENT_INFO_UPDATE:
+ doSegmentUpdate(command.params);
+ break;
case SHUTDOWN:
return (false);
case NOOP:
diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc
index dc03be2..991059b 100644
--- a/src/bin/auth/main.cc
+++ b/src/bin/auth/main.cc
@@ -109,9 +109,11 @@ datasrcConfigHandler(AuthSrv* server, bool* first_time,
assert(config_session != NULL);
*first_time = false;
server->getDataSrcClientsMgr().reconfigure(
- config_session->getRemoteConfigValue("data_sources", "classes"));
+ config_session->getRemoteConfigValue("data_sources", "classes"),
+ boost::bind(&AuthSrv::listsReconfigured, server));
} else if (config->contains("classes")) {
- server->getDataSrcClientsMgr().reconfigure(config->get("classes"));
+ server->getDataSrcClientsMgr().reconfigure(config->get("classes"),
+ boost::bind(&AuthSrv::listsReconfigured, server));
}
}
diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc
index f17d91e..99ad889 100644
--- a/src/bin/auth/tests/auth_srv_unittest.cc
+++ b/src/bin/auth/tests/auth_srv_unittest.cc
@@ -39,6 +39,9 @@
#include <auth/statistics_items.h>
#include <auth/datasrc_config.h>
+#include <config/tests/fake_session.h>
+#include <config/ccsession.h>
+
#include <util/unittests/mock_socketsession.h>
#include <dns/tests/unittest_util.h>
#include <testutils/dnsmessage_test.h>
@@ -265,14 +268,15 @@ updateDatabase(AuthSrv& server, const char* params) {
void
updateInMemory(AuthSrv& server, const char* origin, const char* filename,
- bool with_static = true)
+ bool with_static = true, bool mapped = false)
{
string spec_txt = "{"
"\"IN\": [{"
" \"type\": \"MasterFiles\","
" \"params\": {"
" \"" + string(origin) + "\": \"" + string(filename) + "\""
- " },"
+ " }," +
+ string(mapped ? "\"cache-type\": \"mapped\"," : "") +
" \"cache-enable\": true"
"}]";
if (with_static) {
@@ -2138,4 +2142,42 @@ TEST_F(AuthSrvTest, loadZoneCommand) {
sendCommand(server, "loadzone", args, 0);
}
+// Test that the auth server subscribes to the segment readers group when
+// there's a remotely mapped segment.
+TEST_F(AuthSrvTest, postReconfigure) {
+ FakeSession session(ElementPtr(new ListElement),
+ ElementPtr(new ListElement),
+ ElementPtr(new ListElement));
+ const string specfile(string(TEST_OWN_DATA_DIR) + "/spec.spec");
+ session.getMessages()->add(isc::config::createAnswer());
+ isc::config::ModuleCCSession mccs(specfile, session, NULL, NULL, false,
+ false);
+ server.setConfigSession(&mccs);
+ // First, no lists are there, so no reason to subscribe
+ server.listsReconfigured();
+ EXPECT_FALSE(session.haveSubscription("SegmentReader", "*"));
+ // Enable remote segment
+ updateInMemory(server, "example.", CONFIG_INMEMORY_EXAMPLE, false, true);
+ {
+ DataSrcClientsMgr &mgr(server.getDataSrcClientsMgr());
+ DataSrcClientsMgr::Holder holder(mgr);
+ EXPECT_EQ(SEGMENT_WAITING, holder.findClientList(RRClass::IN())->
+ getStatus()[0].getSegmentState());
+ }
+ server.listsReconfigured();
+ EXPECT_TRUE(session.haveSubscription("SegmentReader", "*"));
+ // Set the segment to local again
+ updateInMemory(server, "example.", CONFIG_INMEMORY_EXAMPLE);
+ {
+ DataSrcClientsMgr &mgr(server.getDataSrcClientsMgr());
+ DataSrcClientsMgr::Holder holder(mgr);
+ EXPECT_EQ(SEGMENT_INUSE, holder.findClientList(RRClass::IN())->
+ getStatus()[0].getSegmentState());
+ EXPECT_EQ("local", holder.findClientList(RRClass::IN())->
+ getStatus()[0].getSegmentType());
+ }
+ server.listsReconfigured();
+ EXPECT_FALSE(session.haveSubscription("SegmentReader", "*"));
+}
+
}
diff --git a/src/bin/auth/tests/datasrc_clients_builder_unittest.cc b/src/bin/auth/tests/datasrc_clients_builder_unittest.cc
index a663db6..ed851b0 100644
--- a/src/bin/auth/tests/datasrc_clients_builder_unittest.cc
+++ b/src/bin/auth/tests/datasrc_clients_builder_unittest.cc
@@ -648,4 +648,106 @@ TEST_F(DataSrcClientsBuilderTest,
TestDataSrcClientsBuilder::InternalCommandError);
}
+// Test the SEGMENT_INFO_UPDATE command. This test is little bit
+// indirect. It doesn't seem possible to fake the client list inside
+// easily. So we create a real image to load and load it. Then we check
+// the segment is used.
+TEST_F(DataSrcClientsBuilderTest,
+#ifdef USE_SHARED_MEMORY
+ segmentInfoUpdate
+#else
+ DISABLED_segmentInfoUpdate
+#endif
+ )
+{
+ // First, prepare the file image to be mapped
+ const ConstElementPtr config = Element::fromJSON(
+ "{"
+ "\"IN\": [{"
+ " \"type\": \"MasterFiles\","
+ " \"params\": {"
+ " \"test1.example\": \""
+ TEST_DATA_BUILDDIR "/test1.zone.copied\"},"
+ " \"cache-enable\": true,"
+ " \"cache-type\": \"mapped\""
+ "}]}");
+ const ConstElementPtr segment_config = Element::fromJSON(
+ "{"
+ " \"mapped-file\": \""
+ TEST_DATA_BUILDDIR "/test1.zone.image" "\"}");
+ clients_map = configureDataSource(config);
+ {
+ const boost::shared_ptr<ConfigurableClientList> list =
+ (*clients_map)[RRClass::IN()];
+ list->resetMemorySegment("MasterFiles",
+ memory::ZoneTableSegment::CREATE,
+ segment_config);
+ const ConfigurableClientList::ZoneWriterPair result =
+ list->getCachedZoneWriter(isc::dns::Name("test1.example"), false,
+ "MasterFiles");
+ ASSERT_EQ(ConfigurableClientList::ZONE_SUCCESS, result.first);
+ result.second->load();
+ result.second->install();
+ // not absolutely necessary, but just in case
+ result.second->cleanup();
+ } // Release this list. That will release the file with the image too,
+ // so we can map it read only from somewhere else.
+
+ // Create a new map, with the same configuration, but without the segments
+ // set
+ clients_map = configureDataSource(config);
+ const boost::shared_ptr<ConfigurableClientList> list =
+ (*clients_map)[RRClass::IN()];
+ EXPECT_EQ(SEGMENT_WAITING, list->getStatus()[0].getSegmentState());
+ // Send the command
+ const ElementPtr command_args = Element::fromJSON(
+ "{"
+ " \"data-source-name\": \"MasterFiles\","
+ " \"data-source-class\": \"IN\""
+ "}");
+ command_args->set("segment-params", segment_config);
+ builder.handleCommand(Command(SEGMENT_INFO_UPDATE, command_args,
+ FinishedCallback()));
+ // The segment is now used.
+ EXPECT_EQ(SEGMENT_INUSE, list->getStatus()[0].getSegmentState());
+
+ // Some invalid inputs (wrong class, different name of data source, etc).
+
+ // Copy the confing and modify
+ const ElementPtr bad_name = Element::fromJSON(command_args->toWire());
+ // Set bad name
+ bad_name->set("data-source-name", Element::create("bad"));
+ EXPECT_DEATH_IF_SUPPORTED({
+ builder.handleCommand(Command(SEGMENT_INFO_UPDATE, bad_name,
+ FinishedCallback()));
+ }, "");
+
+ // Another copy with wrong class
+ const ElementPtr bad_class = Element::fromJSON(command_args->toWire());
+ // Set bad class
+ bad_class->set("data-source-class", Element::create("bad"));
+ // Aborts (we are out of sync somehow).
+ EXPECT_DEATH_IF_SUPPORTED({
+ builder.handleCommand(Command(SEGMENT_INFO_UPDATE, bad_class,
+ FinishedCallback()));
+ }, "");
+
+ // Class CH is valid, but not present.
+ bad_class->set("data-source-class", Element::create("CH"));
+ EXPECT_DEATH_IF_SUPPORTED({
+ builder.handleCommand(Command(SEGMENT_INFO_UPDATE, bad_class,
+ FinishedCallback()));
+ }, "");
+
+ // And break the segment params
+ const ElementPtr bad_params = Element::fromJSON(command_args->toWire());
+ bad_params->set("segment-params",
+ Element::fromJSON("{\"mapped-file\": \"/bad/file\"}"));
+
+ EXPECT_DEATH_IF_SUPPORTED({
+ builder.handleCommand(Command(SEGMENT_INFO_UPDATE, bad_class,
+ FinishedCallback()));
+ }, "");
+}
+
} // unnamed namespace
diff --git a/src/bin/auth/tests/datasrc_clients_mgr_unittest.cc b/src/bin/auth/tests/datasrc_clients_mgr_unittest.cc
index 1bf8101..565deb5 100644
--- a/src/bin/auth/tests/datasrc_clients_mgr_unittest.cc
+++ b/src/bin/auth/tests/datasrc_clients_mgr_unittest.cc
@@ -156,6 +156,7 @@ TEST(DataSrcClientsMgrTest, holder) {
TestDataSrcClientsMgr::Holder holder(mgr);
EXPECT_FALSE(holder.findClientList(RRClass::IN()));
EXPECT_FALSE(holder.findClientList(RRClass::CH()));
+ EXPECT_TRUE(holder.getClasses().empty());
// map should be protected here
EXPECT_EQ(1, FakeDataSrcClientsBuilder::map_mutex->lock_count);
EXPECT_EQ(0, FakeDataSrcClientsBuilder::map_mutex->unlock_count);
@@ -174,6 +175,7 @@ TEST(DataSrcClientsMgrTest, holder) {
TestDataSrcClientsMgr::Holder holder(mgr);
EXPECT_TRUE(holder.findClientList(RRClass::IN()));
EXPECT_TRUE(holder.findClientList(RRClass::CH()));
+ EXPECT_EQ(2, holder.getClasses().size());
}
// We need to clear command queue by hand
FakeDataSrcClientsBuilder::command_queue->clear();
@@ -188,6 +190,7 @@ TEST(DataSrcClientsMgrTest, holder) {
TestDataSrcClientsMgr::Holder holder(mgr);
EXPECT_TRUE(holder.findClientList(RRClass::IN()));
EXPECT_FALSE(holder.findClientList(RRClass::CH()));
+ EXPECT_EQ(RRClass::IN(), holder.getClasses()[0]);
}
// Duplicate lock acquisition is prohibited (only test mgr can detect
@@ -245,6 +248,30 @@ TEST(DataSrcClientsMgrTest, reload) {
EXPECT_EQ(3, FakeDataSrcClientsBuilder::command_queue->size());
}
+TEST(DataSrcClientsMgrTest, segmentUpdate) {
+ TestDataSrcClientsMgr mgr;
+ EXPECT_TRUE(FakeDataSrcClientsBuilder::started);
+ EXPECT_TRUE(FakeDataSrcClientsBuilder::command_queue->empty());
+
+ isc::data::ElementPtr args =
+ isc::data::Element::fromJSON("{\"data-source-class\": \"IN\","
+ " \"data-source-name\": \"sqlite3\","
+ " \"segment-params\": {}}");
+ mgr.segmentInfoUpdate(args);
+ EXPECT_EQ(1, FakeDataSrcClientsBuilder::command_queue->size());
+ // Some invalid inputs
+ EXPECT_THROW(mgr.segmentInfoUpdate(isc::data::Element::fromJSON(
+ "{\"data-source-class\": \"IN\","
+ " \"data-source-name\": \"sqlite3\"}")), CommandError);
+ EXPECT_THROW(mgr.segmentInfoUpdate(isc::data::Element::fromJSON(
+ "{\"data-source-name\": \"sqlite3\","
+ " \"segment-params\": {}}")), CommandError);
+ EXPECT_THROW(mgr.segmentInfoUpdate(isc::data::Element::fromJSON(
+ "{\"data-source-class\": \"IN\","
+ " \"segment-params\": {}}")), CommandError);
+ EXPECT_EQ(1, FakeDataSrcClientsBuilder::command_queue->size());
+}
+
void
callback(bool* called, int *tag_target, int tag_value) {
*called = true;
diff --git a/src/lib/config/ccsession.cc b/src/lib/config/ccsession.cc
index 0d43b27..780fe95 100644
--- a/src/lib/config/ccsession.cc
+++ b/src/lib/config/ccsession.cc
@@ -595,6 +595,8 @@ ModuleCCSession::checkModuleCommand(const std::string& cmd_str,
"Command given but no "
"command handler for module"));
}
+ } else if (unhandled_callback_) {
+ unhandled_callback_(cmd_str, target_module, arg);
}
return (ElementPtr());
}
diff --git a/src/lib/config/ccsession.h b/src/lib/config/ccsession.h
index 04e3046..c536861 100644
--- a/src/lib/config/ccsession.h
+++ b/src/lib/config/ccsession.h
@@ -575,6 +575,50 @@ public:
/// \param id The id of request as returned by groupRecvMsgAsync.
void cancelAsyncRecv(const AsyncRecvRequestID& id);
+ /// \brief Subscribe to a group
+ ///
+ /// Wrapper around the CCSession::subscribe.
+ void subscribe(const std::string& group) {
+ session_.subscribe(group, isc::cc::CC_INSTANCE_WILDCARD);
+ }
+
+ /// \brief Unsubscribe from a group.
+ ///
+ /// Wrapper around the CCSession::unsubscribe.
+ void unsubscribe(const std::string& group) {
+ session_.unsubscribe(group, isc::cc::CC_INSTANCE_WILDCARD);
+ }
+
+ /// \brief Callback type for unhandled commands
+ ///
+ /// The type of functions that are not handled by the ModuleCCSession
+ /// because they are not aimed at the module.
+ ///
+ /// The parameters are:
+ /// - Name of the command.
+ /// - The module it was aimed for (may be empty).
+ /// - The parameters of the command.
+ typedef boost::function<void (const std::string&, const std::string&,
+ const isc::data::ConstElementPtr&)>
+ UnhandledCallback;
+
+ /// \brief Register a callback for messages sent to foreign modules.
+ ///
+ /// Usually, a command aimed at foreign module (or sent directly)
+ /// is discarded. By registering a callback here, these can be
+ /// examined.
+ ///
+ /// \note A callback overwrites the previous one set.
+ /// \todo This is a temporary, unclean, solution. A more generic
+ /// one needs to be designed. Also, a solution that is able
+ /// to send an answer would be great.
+ ///
+ /// \param callback The new callback to use. It may be an empty
+ /// function.
+ void setUnhandledCallback(const UnhandledCallback& callback) {
+ unhandled_callback_ = callback;
+ }
+
private:
ModuleSpec readModuleSpecification(const std::string& filename);
void startCheck();
@@ -624,6 +668,8 @@ private:
isc::data::ConstElementPtr new_config);
ModuleSpec fetchRemoteSpec(const std::string& module, bool is_filename);
+
+ UnhandledCallback unhandled_callback_;
};
/// \brief Default handler for logging config updates
diff --git a/src/lib/config/tests/ccsession_unittests.cc b/src/lib/config/tests/ccsession_unittests.cc
index 700ffe4..4a04412 100644
--- a/src/lib/config/tests/ccsession_unittests.cc
+++ b/src/lib/config/tests/ccsession_unittests.cc
@@ -157,6 +157,17 @@ TEST_F(CCSessionTest, notifyNoParams) {
session.getMsgQueue()->get(1)->toWire();
}
+// Try to subscribe and unsubscribe once again
+TEST_F(CCSessionTest, subscribe) {
+ ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL, false,
+ false);
+ EXPECT_FALSE(session.haveSubscription("A group", "*"));
+ mccs.subscribe("A group");
+ EXPECT_TRUE(session.haveSubscription("A group", "*"));
+ mccs.unsubscribe("A group");
+ EXPECT_FALSE(session.haveSubscription("A group", "*"));
+}
+
TEST_F(CCSessionTest, createAnswer) {
ConstElementPtr answer;
answer = createAnswer();
@@ -686,6 +697,16 @@ TEST_F(CCSessionTest, remoteConfig) {
}
}
+void
+callback(std::string* command, std::string* target, ConstElementPtr *params,
+ const std::string& command_real, const std::string& target_real,
+ const ConstElementPtr& params_real)
+{
+ *command = command_real;
+ *target = target_real;
+ *params = params_real;
+}
+
TEST_F(CCSessionTest, ignoreRemoteConfigCommands) {
// client will ask for config
session.getMessages()->add(createAnswer(0, el("{ }")));
@@ -721,6 +742,22 @@ TEST_F(CCSessionTest, ignoreRemoteConfigCommands) {
result = mccs.checkCommand();
EXPECT_EQ(0, session.getMsgQueue()->size());
EXPECT_EQ(0, result);
+
+ // Check that we can get the ignored commands by registering a callback
+ std::string command, target;
+ ConstElementPtr params;
+ mccs.setUnhandledCallback(boost::bind(&callback, &command, &target,
+ ¶ms, _1, _2, _3));
+ session.addMessage(el("{ \"command\": [ \"good_command\","
+ "{\"param\": true} ] }"), "Spec1", "*");
+ EXPECT_EQ(1, session.getMsgQueue()->size());
+ result = mccs.checkCommand();
+ EXPECT_EQ(0, session.getMsgQueue()->size());
+ EXPECT_EQ(0, result);
+
+ EXPECT_EQ("good_command", command);
+ EXPECT_EQ("Spec1", target);
+ EXPECT_TRUE(params && el("{\"param\": true}")->equals(*params));
}
TEST_F(CCSessionTest, initializationFail) {
diff --git a/src/lib/datasrc/client_list.cc b/src/lib/datasrc/client_list.cc
index 4fe1eb3..531821c 100644
--- a/src/lib/datasrc/client_list.cc
+++ b/src/lib/datasrc/client_list.cc
@@ -330,7 +330,7 @@ ConfigurableClientList::findInternal(MutableResult& candidate,
// and the need_updater parameter is true, get the zone there.
}
-void
+bool
ConfigurableClientList::resetMemorySegment
(const std::string& datasrc_name,
ZoneTableSegment::MemorySegmentOpenMode mode,
@@ -340,9 +340,10 @@ ConfigurableClientList::resetMemorySegment
if (info.name_ == datasrc_name) {
ZoneTableSegment& segment = *info.ztable_segment_;
segment.reset(mode, config_params);
- break;
+ return true;
}
}
+ return false;
}
ConfigurableClientList::ZoneWriterPair
diff --git a/src/lib/datasrc/client_list.h b/src/lib/datasrc/client_list.h
index e4df415..77c2fd5 100644
--- a/src/lib/datasrc/client_list.h
+++ b/src/lib/datasrc/client_list.h
@@ -385,7 +385,8 @@ public:
/// \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
+ /// \return If the data source was found and reset.
+ bool resetMemorySegment
(const std::string& datasrc_name,
memory::ZoneTableSegment::MemorySegmentOpenMode mode,
isc::data::ConstElementPtr config_params);
@@ -453,8 +454,6 @@ public:
bool want_finder = true) const;
/// \brief This holds one data source client and corresponding information.
- ///
- /// \todo The content yet to be defined.
struct DataSourceInfo {
DataSourceInfo(DataSourceClient* data_src_client,
const DataSourceClientContainerPtr& container,
@@ -526,7 +525,7 @@ public:
/// This may throw standard exceptions, such as std::bad_alloc. Otherwise,
/// it is exception free.
std::vector<DataSourceStatus> getStatus() const;
-public:
+
/// \brief Access to the data source clients.
///
/// It can be used to examine the loaded list of data sources clients
diff --git a/src/lib/datasrc/tests/client_list_unittest.cc b/src/lib/datasrc/tests/client_list_unittest.cc
index eb69556..256e2ed 100644
--- a/src/lib/datasrc/tests/client_list_unittest.cc
+++ b/src/lib/datasrc/tests/client_list_unittest.cc
@@ -368,7 +368,8 @@ public:
const std::string& datasrc_name,
ZoneTableSegment::MemorySegmentOpenMode mode,
ConstElementPtr config_params) {
- list.resetMemorySegment(datasrc_name, mode, config_params);
+ EXPECT_TRUE(list.resetMemorySegment(datasrc_name, mode,
+ config_params));
}
virtual std::string getType() {
return ("mapped");
@@ -383,6 +384,13 @@ INSTANTIATE_TEST_CASE_P(ListTestMapped, ListTest,
#endif
+// Calling reset on empty list finds no data and returns false.
+TEST_P(ListTest, emptyReset) {
+ EXPECT_FALSE(list_->resetMemorySegment("Something",
+ memory::ZoneTableSegment::CREATE,
+ Element::create()));
+}
+
// Test the test itself
TEST_P(ListTest, selfTest) {
EXPECT_EQ(result::SUCCESS, ds_[0]->findZone(Name("example.org")).code);
More information about the bind10-changes
mailing list