BIND 10 trac2738, updated. c1973ca0b5cccc02c53656645e058e1e3ab009b9 [2738] Merge branch 'trac2833' into trac2738

BIND 10 source code commits bind10-changes at lists.isc.org
Wed Apr 3 20:51:50 UTC 2013


The branch, trac2738 has been updated
       via  c1973ca0b5cccc02c53656645e058e1e3ab009b9 (commit)
       via  ec52d37b4554e01d70afa67c64d203c445a9aac6 (commit)
       via  ce1d57a6341ca867ce6624424043b13cfa3d5f39 (commit)
       via  46168c33b8ff9bdc8b5448c350f2037453e0c2d9 (commit)
       via  339e9f7077179abbeeb3a3f16a6f1f95cb22786c (commit)
       via  32c1b0777e781501e347ac160e176d2ab6ddcfad (commit)
       via  9a8bc5b083049e6d42fe38397ea44375d8634020 (commit)
       via  0363b4187fe3c1a148ad424af39e12846610d2d7 (commit)
       via  1a11b2d51ccff009c2caf4e132ac18e5bf92b11b (commit)
       via  63c8beeb9a4751f512ade6c72e9dc1ce7ed5f158 (commit)
       via  d9f12c0e4d904fed50e43d5602c4d97563d701d7 (commit)
       via  517ca63bd5adda6a2a2f40c889a87f1bf8f7832e (commit)
       via  80d60b6b5653d581c6cfe1fb05a4128eacc4e1d6 (commit)
       via  879fb8f585a5fe1af737ad41ea81e35a4cc0cfa4 (commit)
       via  e8137e183ee0b94ccc82d47d1489a470d551bff6 (commit)
       via  347f81283182675265cda722d1def0bccedf4c7b (commit)
       via  94a17934a24d94741b53857d17fcfb70a0cb1ed6 (commit)
       via  766d7f47299cea598fd47bb510b9366de6da4356 (commit)
       via  00519912eb96e6fe6aad91376be073977b350f4b (commit)
       via  15962b4badfed5d448826d2d55e8542314498dee (commit)
       via  71e772824618ff3399ed6b2c6d1e24103fb8bc29 (commit)
       via  d47e9e7db10822b6197d7be175a0146e8252ead0 (commit)
       via  de096122c7d18fae6f0afed28ccdf0bbfcf65395 (commit)
       via  2cc9047529f689aa7e1cb08e2d6321c2e1b76049 (commit)
       via  d2805586d949f91b9ca649206a1f4b03d7da4193 (commit)
       via  745c28556fd0d85718e7228a4957448b2803a348 (commit)
       via  905f265367c7b4ee5486886faf2951cb655fa60e (commit)
       via  201899adaf91b1c8f7affff546c0c00de7746eab (commit)
       via  4d3f1c82605bc45fd390ee3829dff1c3bff0cab6 (commit)
       via  ad797b98b8ae4f0f003374c13649147aef3c1a83 (commit)
       via  8802d414240a4b453b13573e1bbc381eb0d14343 (commit)
       via  b321b2e7708069b82bae12eb1b4461f4eb9e4a03 (commit)
       via  fe630ca4be50f0f0bd048eec264b8a21df9c6b38 (commit)
       via  7f62c19c5d561e755570b8aeab625dd275ecae3a (commit)
       via  4c90b6cf817c2087052ea8b22e91a3b7f8ae39f0 (commit)
       via  0107d1f09fb6075c9fc2ea6a162c85d4a89a8e85 (commit)
       via  bcefe1e95cdd61ee4a09b20522c3c56b315a1acc (commit)
       via  37583984483d4027f5e2fece88e3f7e12ac42fe8 (commit)
       via  ad11aede925c7307dd3a6528be0d1e8e4723d162 (commit)
       via  d72fddedb47c7dfae908e2c5bfc9c6ca1d574420 (commit)
       via  37e3ef0278c0f52a2e211b72d9b39e7e25262b43 (commit)
       via  75af5cd564de8cec47f2ed5ab3b5cb028acdd0ca (commit)
       via  1073338ae5e5ea9fa385abf5b87ff3421f51ab97 (commit)
       via  1450d8d486cba3bee8be46e8001d66898edd370c (commit)
       via  451d16ebbbf619ad5b420b7a837bf95ec23ab686 (commit)
       via  954430281cec4d8f6382285f761a1859ca9508f7 (commit)
       via  93cb134fd8ff6fc6c9d63d0a9f6ca3defed43721 (commit)
       via  da42298dc1ad39a5073bb8b221e05ccf8fb5464c (commit)
       via  94aade43c190a9dccca5469009b2604efb7c5664 (commit)
       via  f3507e17b575821adf1076236e2855c1d7be1f26 (commit)
       via  c5f8c4c31d08049b5b8b1e7ab43011d62293f18b (commit)
       via  d3a0ac2204c90e1bb6b3ccb474f7224f1154a0db (commit)
       via  4660c86fe6aeb2c77427ca8cacd1b7f840d4e554 (commit)
       via  946126b36db6b9cd21c194a84a61417dd6bd567c (commit)
       via  8c6afd880d47f33174b656ed12a2e5e4c821e14b (commit)
       via  22407c48db5999e77a6fbd6e4f42ba2a3fe36762 (commit)
       via  bc87d1113832e7900d3627663c0a1203c3e7b2da (commit)
       via  6fadd98cb5a9beeb79c58ba0a2768d9ad895c210 (commit)
       via  9a551ccda02b841bf296ecbce5c0bbf04721024a (commit)
       via  716006fe1be4edaf841b94d488cd1d8cbbda116e (commit)
       via  e1c60036dfd2d4e1fcd372807e4a3f0f201028cb (commit)
       via  2118111e02bb90ffc92e14d37681acf3c5f52b98 (commit)
       via  d66a48434bce9de43f371bb37811c820634f9937 (commit)
       via  c178521c5c7133093866a8403b3d1d8af2cb6684 (commit)
       via  60635a58548ae680058d67837e6ce1696c3b7faa (commit)
       via  660990cbc4df062e7814f86040f7cf87662190c2 (commit)
       via  8c4c6e96fb6289bbacc2e4d15b8a7b131c8fadd8 (commit)
       via  191f569b711f30366f584045e4cefd9211929ad8 (commit)
       via  f687f77020bc9c4619915e7bb1d7f5a7c6c90ccc (commit)
       via  6e0725b08ad12bafcc939622daa5a2f7b734ecbe (commit)
       via  8377d0dcf3c758cc50111a98a7a9dafdc3237c44 (commit)
       via  5e9811640dc3e514ee012b16287522f7993ade46 (commit)
       via  d007842f66f874ad8ceacf664f8c0645d2ec4226 (commit)
       via  d25f0e1e01a6cc3991ae63f0957c61ec171395ce (commit)
       via  0be5bed016c3b12abf5e7049ab7b63d2d474efc0 (commit)
       via  2e6d5f8d602a632d8a770c13d7ccbc09a25825ad (commit)
       via  7642e09748501d041309b84de9d46b3aeab74a63 (commit)
       via  9dd3b10a7ce79fb1b1289136987f82c2bf9c2310 (commit)
       via  739735c08bb30957be78506bc4cf0d6c5f01745d (commit)
       via  01e052f372b1878b9bc40bfcea80d37e408a58ca (commit)
       via  25b99148984082fd818624cd74cde2d86d428c2b (commit)
       via  97a7be5ddb8f5a89206912eae174bacf3d230a8e (commit)
       via  f37600d5ae721b626b9e9323dbff17a404908e46 (commit)
      from  f0f75ecda5cb7e51fbebbcf35ca40f3d38b4b535 (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 c1973ca0b5cccc02c53656645e058e1e3ab009b9
Merge: f0f75ec ec52d37
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed Apr 3 12:55:45 2013 -0700

    [2738] Merge branch 'trac2833' into trac2738

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog                                          |   15 ++
 README                                             |    6 +
 examples/host/host.cc                              |    2 +-
 src/bin/auth/tests/auth_srv_unittest.cc            |  100 ++------
 src/bin/bind10/init.py.in                          |    3 +-
 src/bin/bind10/init_messages.mes                   |    4 +-
 src/bin/bind10/tests/init_test.py.in               |   10 +
 src/bin/cfgmgr/plugins/datasrc.spec.pre.in         |   11 +-
 src/bin/cfgmgr/plugins/tests/Makefile.am           |    3 +
 src/bin/cfgmgr/plugins/tests/datasrc_test.py       |   95 +++++++
 src/bin/cmdctl/cmdctl.py.in                        |    4 +-
 src/bin/msgq/Makefile.am                           |    1 +
 src/bin/msgq/msgq.py.in                            |   29 +--
 src/bin/stats/stats_httpd.py.in                    |    9 +-
 src/bin/stats/stats_httpd_messages.mes             |    4 +-
 src/bin/zonemgr/zonemgr.py.in                      |   14 +-
 src/lib/cc/data.h                                  |    2 +-
 src/lib/cc/proto_defs.cc                           |   10 +
 src/lib/cc/session.cc                              |   48 ++--
 src/lib/config/ccsession.cc                        |   45 ++--
 src/lib/config/ccsession.h                         |    4 +-
 src/lib/datasrc/Makefile.am                        |    9 +-
 src/lib/datasrc/cache_config.cc                    |  113 +++++++++
 src/lib/datasrc/cache_config.h                     |  172 +++++++++++++
 src/lib/datasrc/client_list.cc                     |  141 ++++++-----
 src/lib/datasrc/client_list.h                      |  108 +++++++-
 src/lib/datasrc/datasrc_messages.mes               |   12 +-
 src/lib/datasrc/memory/zone_table_segment.cc       |   18 +-
 src/lib/datasrc/memory/zone_table_segment.h        |   48 ++--
 src/lib/datasrc/static_datasrc.h                   |   50 ----
 src/lib/datasrc/static_datasrc_link.cc             |   68 -----
 src/lib/datasrc/tests/Makefile.am                  |    2 +
 src/lib/datasrc/tests/cache_config_unittest.cc     |  241 ++++++++++++++++++
 src/lib/datasrc/tests/client_list_unittest.cc      |  263 ++++++++------------
 src/lib/datasrc/tests/factory_unittest.cc          |   52 ----
 .../tests/memory/zone_table_segment_unittest.cc    |    7 +-
 .../datasrc/tests/memory/zone_writer_unittest.cc   |    7 +-
 src/lib/datasrc/tests/mock_client.cc               |  197 +++++++++++++++
 src/lib/datasrc/tests/mock_client.h                |   71 ++++++
 .../datasrc/tests/zone_finder_context_unittest.cc  |    6 +-
 src/lib/datasrc/tests/zone_loader_unittest.cc      |   42 ++--
 src/lib/dhcpsrv/pool.cc                            |   11 +-
 src/lib/dhcpsrv/pool.h                             |    5 +-
 src/lib/dns/gen-rdatacode.py.in                    |    1 +
 src/lib/dns/rdata/generic/afsdb_18.cc              |   67 +++--
 src/lib/dns/rdata/generic/afsdb_18.h               |    4 +-
 src/lib/dns/tests/rdata_afsdb_unittest.cc          |   37 ++-
 src/lib/python/isc/cc/Makefile.am                  |    1 +
 src/lib/python/isc/cc/cc_generated/Makefile.am     |    1 +
 src/lib/python/isc/cc/session.py                   |   43 ++--
 src/lib/python/isc/config/ccsession.py             |   35 +--
 src/lib/python/isc/config/cfgmgr.py                |    3 +-
 src/lib/python/isc/datasrc/tests/datasrc_test.py   |   23 +-
 .../python/isc/datasrc/tests/zone_loader_test.py   |   26 +-
 src/lib/python/isc/notify/notify_out.py            |   38 +--
 src/lib/python/isc/notify/notify_out_messages.mes  |   22 +-
 src/lib/python/isc/util/Makefile.am                |    3 +-
 src/lib/python/isc/util/address_formatter.py       |   82 ++++++
 src/lib/python/isc/util/tests/Makefile.am          |    3 +-
 .../isc/util/tests/address_formatter_test.py       |   68 +++++
 src/lib/server_common/client.cc                    |   10 +-
 src/lib/server_common/client.h                     |    2 +-
 src/lib/server_common/portconfig.cc                |    8 +-
 src/lib/server_common/server_common_messages.mes   |    2 +-
 src/lib/server_common/tests/client_unittest.cc     |    4 +-
 tests/lettuce/README                               |    5 +
 tests/lettuce/features/terrain/terrain.py          |    8 +-
 67 files changed, 1785 insertions(+), 773 deletions(-)
 create mode 100644 src/lib/datasrc/cache_config.cc
 create mode 100644 src/lib/datasrc/cache_config.h
 delete mode 100644 src/lib/datasrc/static_datasrc.h
 delete mode 100644 src/lib/datasrc/static_datasrc_link.cc
 create mode 100644 src/lib/datasrc/tests/cache_config_unittest.cc
 create mode 100644 src/lib/datasrc/tests/mock_client.cc
 create mode 100644 src/lib/datasrc/tests/mock_client.h
 create mode 100644 src/lib/python/isc/util/address_formatter.py
 create mode 100644 src/lib/python/isc/util/tests/address_formatter_test.py

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index be8cc34..f2b742c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+594.	[func]		muks, pselkirk
+	libdns++: the NSEC, DS, DLV, and AFSDB Rdata classes now use the
+	generic lexer in constructors from text.  This means that the name
+	fields in such RRs in a zone file can now be non-absolute (the
+	origin name in that context will be used), e.g., when loaded by
+	b10-loadzone.
+	(Trac #2386, git dc0f34afb1eccc574421a802557198e6cd2363fa)
+	(Trac #2391, git 1450d8d486cba3bee8be46e8001d66898edd370c)
+
+593.	[func]		jelte
+	Address + port output and logs is now consistent according to our
+	coding guidelines, e.g. <address>:<port> in the case of IPv4, and
+	[<address>]:<port> in the case of IPv6, instead of <address>#<port>
+	(Trac #1086, git bcefe1e95cdd61ee4a09b20522c3c56b315a1acc)
+
 592.	[bug]		jinmei
 	b10-auth and zonemgr now handle some uncommon NOTIFY messages more
 	gracefully: auth immediately returns a NOTAUTH response if the
diff --git a/README b/README
index 70c6bee..f9b24c5 100644
--- a/README
+++ b/README
@@ -25,6 +25,12 @@ the DHCPv4 and DHCPv6 servers must be considered experimental.
 Limitations and known issues with this DHCP release can be found
 at http://bind10.isc.org/wiki/KeaKnownIssues
 
+NOTE: The API/ABI provided by libraries in BIND 10 may change in future
+point releases. So please do not assume currently that any code that you
+compile for a particular version of a BIND 10 library will work in
+future versions of the library. We aim to stabilize the public API/ABI
+interface of BIND 10 libraries in future releases.
+
 Documentation is included with the source. See doc/guide/bind10-guide.txt
 (or bind10-guide.html) for installation instructions.  The
 documentation is also available via the BIND 10 website at
diff --git a/examples/host/host.cc b/examples/host/host.cc
index a5c6522..c5dd604 100644
--- a/examples/host/host.cc
+++ b/examples/host/host.cc
@@ -95,7 +95,7 @@ host_lookup(const char* const name, const char* const dns_class,
         cout << "Name: " << server << "\n";
         // TODO: I guess I have to do a lookup to get that address and aliases
         // too
-        //cout << "Address: " << address << "\n" ; // "#" << port << "\n";
+        //cout << "Address: " << address << "\n" ;
         //cout << "Aliases: " << server << "\n";
     }
 
diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc
index 931d1e2..6b283d5 100644
--- a/src/bin/auth/tests/auth_srv_unittest.cc
+++ b/src/bin/auth/tests/auth_srv_unittest.cc
@@ -76,7 +76,6 @@ using namespace isc::asiolink;
 using namespace isc::testutils;
 using namespace isc::server_common::portconfig;
 using namespace isc::auth::unittest;
-using isc::datasrc::memory::ZoneTableSegment;
 using isc::UnitTestUtil;
 using boost::scoped_ptr;
 using isc::auth::statistics::Counters;
@@ -262,8 +261,6 @@ updateDatabase(AuthSrv& server, const char* params) {
     installDataSrcClientLists(server, configureDataSource(config));
 }
 
-// Note: if with_static is set to true, the corresponding test should be
-// disabled in case of USE_STATIC_LINK.
 void
 updateInMemory(AuthSrv& server, const char* origin, const char* filename,
                bool with_static = true)
@@ -278,8 +275,9 @@ updateInMemory(AuthSrv& server, const char* origin, const char* filename,
         "}]";
     if (with_static) {
         spec_txt += ", \"CH\": [{"
-        "   \"type\": \"static\","
-        "   \"params\": \"" + string(STATIC_DSRC_FILE) + "\""
+        "   \"type\": \"MasterFiles\","
+        "   \"cache-enable\": true,"
+        "   \"params\": {\"BIND\": \"" + string(STATIC_DSRC_FILE) + "\"}"
             "}]";
     }
     spec_txt += "}";
@@ -288,14 +286,13 @@ updateInMemory(AuthSrv& server, const char* origin, const char* filename,
     installDataSrcClientLists(server, configureDataSource(config));
 }
 
-// Note: tests using this function should be disabled in case of
-// USE_STATIC_LINK.
 void
 updateBuiltin(AuthSrv& server) {
     const ConstElementPtr config(Element::fromJSON("{"
         "\"CH\": [{"
-        "   \"type\": \"static\","
-        "   \"params\": \"" + string(STATIC_DSRC_FILE) + "\""
+        "   \"type\": \"MasterFiles\","
+        "   \"cache-enable\": true,"
+        "   \"params\": {\"BIND\": \"" + string(STATIC_DSRC_FILE) + "\"}"
         "}]}"));
     installDataSrcClientLists(server, configureDataSource(config));
 }
@@ -748,11 +745,7 @@ TEST_F(AuthSrvTest, notify) {
     checkStatisticsCounters(stats_after, expect);
 }
 
-#ifdef USE_STATIC_LINK
-TEST_F(AuthSrvTest, DISABLED_notifyForCHClass) {
-#else
 TEST_F(AuthSrvTest, notifyForCHClass) {
-#endif
     // Same as the previous test, but for the CH RRClass (so we install the
     // builtin (static) data source.
     updateBuiltin(server);
@@ -996,11 +989,7 @@ TEST_F(AuthSrvTest, notifyNotAuthNoClass) {
 
 // Try giving the server a TSIG signed request and see it can anwer signed as
 // well
-#ifdef USE_STATIC_LINK
-TEST_F(AuthSrvTest, DISABLED_TSIGSigned) { // Needs builtin
-#else
 TEST_F(AuthSrvTest, TSIGSigned) {
-#endif
     // Prepare key, the client message, etc
     updateBuiltin(server);
     const TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
@@ -1058,11 +1047,7 @@ TEST_F(AuthSrvTest, TSIGSigned) {
 // authoritative only server in terms of performance, and it's quite likely
 // we need to drop it for the authoritative server implementation.
 // At that point we can drop this test, too.
-#ifdef USE_STATIC_LINK
-TEST_F(AuthSrvTest, DISABLED_builtInQueryViaDNSServer) {
-#else
 TEST_F(AuthSrvTest, builtInQueryViaDNSServer) {
-#endif
     updateBuiltin(server);
     UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
                                        default_qid, Name("VERSION.BIND."),
@@ -1090,11 +1075,7 @@ TEST_F(AuthSrvTest, builtInQueryViaDNSServer) {
 
 // The most primitive check: checking the result of the processMessage()
 // method
-#ifdef USE_STATIC_LINK
-TEST_F(AuthSrvTest, DISABLED_builtInQuery) {
-#else
 TEST_F(AuthSrvTest, builtInQuery) {
-#endif
     updateBuiltin(server);
     UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
                                        default_qid, Name("VERSION.BIND."),
@@ -1111,11 +1092,7 @@ TEST_F(AuthSrvTest, builtInQuery) {
 }
 
 // Same type of test as builtInQueryViaDNSServer but for an error response.
-#ifdef USE_STATIC_LINK
-TEST_F(AuthSrvTest, DISABLED_iqueryViaDNSServer) { // Needs builtin
-#else
-TEST_F(AuthSrvTest, iqueryViaDNSServer) { // Needs builtin
-#endif
+TEST_F(AuthSrvTest, iqueryViaDNSServer) {
     updateBuiltin(server);
     createDataFromFile("iquery_fromWire.wire");
     (*server.getDNSLookupProvider())(*io_message, parse_message,
@@ -1226,11 +1203,7 @@ TEST_F(AuthSrvTest, updateWithInMemoryClient) {
                 opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
 }
 
-#ifdef USE_STATIC_LINK
-TEST_F(AuthSrvTest, DISABLED_queryWithInMemoryClientNoDNSSEC) {
-#else
 TEST_F(AuthSrvTest, queryWithInMemoryClientNoDNSSEC) {
-#endif
     // In this example, we do simple check that query is handled from the
     // query handler class, and confirm it returns no error and a non empty
     // answer section.  Detailed examination on the response content
@@ -1246,11 +1219,7 @@ TEST_F(AuthSrvTest, queryWithInMemoryClientNoDNSSEC) {
                 opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 2, 1);
 }
 
-#ifdef USE_STATIC_LINK
-TEST_F(AuthSrvTest, DISABLED_queryWithInMemoryClientDNSSEC) {
-#else
 TEST_F(AuthSrvTest, queryWithInMemoryClientDNSSEC) {
-#endif
     // Similar to the previous test, but the query has the DO bit on.
     // The response should contain RRSIGs, and should have more RRs than
     // the previous case.
@@ -1265,14 +1234,7 @@ TEST_F(AuthSrvTest, queryWithInMemoryClientDNSSEC) {
                 opcode.getCode(), QR_FLAG | AA_FLAG, 1, 2, 3, 3);
 }
 
-TEST_F(AuthSrvTest,
-#ifdef USE_STATIC_LINK
-       DISABLED_chQueryWithInMemoryClient
-#else
-       chQueryWithInMemoryClient
-#endif
-    )
-{
+TEST_F(AuthSrvTest, chQueryWithInMemoryClient) {
     // Set up the in-memory
     updateInMemory(server, "example.", CONFIG_INMEMORY_EXAMPLE);
 
@@ -1745,9 +1707,7 @@ public:
              real_list, ThrowWhen throw_when, bool isc_exception,
              ConstRRsetPtr fake_rrset = ConstRRsetPtr()) :
         ConfigurableClientList(RRClass::IN()),
-        real_(real_list),
-        config_(Element::fromJSON("{}")),
-        ztable_segment_(ZoneTableSegment::create(*config_, RRClass::IN()))
+        real_(real_list)
     {
         BOOST_FOREACH(const DataSourceInfo& info, real_->getDataSources()) {
              const isc::datasrc::DataSourceClientPtr
@@ -1759,13 +1719,13 @@ public:
              data_sources_.push_back(
                  DataSourceInfo(client.get(),
                                 isc::datasrc::DataSourceClientContainerPtr(),
-                                false, RRClass::IN(), ztable_segment_));
+                                boost::shared_ptr<
+                                isc::datasrc::internal::CacheConfig>(),
+                                RRClass::IN(), ""));
         }
     }
 private:
     const boost::shared_ptr<isc::datasrc::ConfigurableClientList> real_;
-    const ConstElementPtr config_;
-    boost::shared_ptr<ZoneTableSegment> ztable_segment_;
     vector<isc::datasrc::DataSourceClientPtr> clients_;
 };
 
@@ -1775,14 +1735,7 @@ private:
 //
 // Set the proxies to never throw, this should have the same result as
 // queryWithInMemoryClientNoDNSSEC, and serves to test the two proxy classes
-TEST_F(AuthSrvTest,
-#ifdef USE_STATIC_LINK
-       DISABLED_queryWithInMemoryClientProxy
-#else
-       queryWithInMemoryClientProxy
-#endif
-    )
-{
+TEST_F(AuthSrvTest, queryWithInMemoryClientProxy) {
     // Set real inmem client to proxy
     updateInMemory(server, "example.", CONFIG_INMEMORY_EXAMPLE);
     boost::shared_ptr<isc::datasrc::ConfigurableClientList> list;
@@ -1829,14 +1782,7 @@ setupThrow(AuthSrv& server, ThrowWhen throw_when, bool isc_exception,
     mgr.setDataSrcClientLists(lists);
 }
 
-TEST_F(AuthSrvTest,
-#ifdef USE_STATIC_LINK
-       DISABLED_queryWithThrowingProxyServfails
-#else
-       queryWithThrowingProxyServfails
-#endif
-    )
-{
+TEST_F(AuthSrvTest, queryWithThrowingProxyServfails) {
     // Test the common cases, all of which should simply return SERVFAIL
     // Use THROW_NEVER as end marker
     ThrowWhen throws[] = { THROW_AT_FIND_ZONE,
@@ -1860,14 +1806,7 @@ TEST_F(AuthSrvTest,
 
 // Throw isc::Exception in getClass(). (Currently?) getClass is not called
 // in the processMessage path, so this should result in a normal answer
-TEST_F(AuthSrvTest,
-#ifdef USE_STATIC_LINK
-       DISABLED_queryWithInMemoryClientProxyGetClass
-#else
-       queryWithInMemoryClientProxyGetClass
-#endif
-    )
-{
+TEST_F(AuthSrvTest, queryWithInMemoryClientProxyGetClass) {
     createDataFromFile("nsec3query_nodnssec_fromWire.wire");
     setupThrow(server, THROW_AT_GET_CLASS, true);
 
@@ -1880,14 +1819,7 @@ TEST_F(AuthSrvTest,
                 opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 2, 1);
 }
 
-TEST_F(AuthSrvTest,
-#ifdef USE_STATIC_LINK
-       DISABLED_queryWithThrowingInToWire
-#else
-       queryWithThrowingInToWire
-#endif
-    )
-{
+TEST_F(AuthSrvTest, queryWithThrowingInToWire) {
     // Set up a faked data source.  It will return an empty RRset for the
     // query.
     ConstRRsetPtr empty_rrset(new RRset(Name("foo.example"),
diff --git a/src/bin/bind10/init.py.in b/src/bin/bind10/init.py.in
index 62a5e2d..e57971a 100755
--- a/src/bin/bind10/init.py.in
+++ b/src/bin/bind10/init.py.in
@@ -38,6 +38,7 @@ __main__.
 
 import sys; sys.path.append ('@@PYTHONPATH@@')
 import os
+from isc.util.address_formatter import AddressFormatter
 
 # If B10_FROM_SOURCE is set in the environment, we use data files
 # from a directory relative to that, otherwise we use the ones
@@ -428,7 +429,7 @@ class Init:
                         port)
         else:
             logger.info(BIND10_STARTING_PROCESS_PORT_ADDRESS,
-                        self.curproc, address, port)
+                        self.curproc, AddressFormatter((address, port)))
 
     def log_started(self, pid = None):
         """
diff --git a/src/bin/bind10/init_messages.mes b/src/bin/bind10/init_messages.mes
index e4e354c..267790d 100644
--- a/src/bin/bind10/init_messages.mes
+++ b/src/bin/bind10/init_messages.mes
@@ -285,9 +285,9 @@ The b10-init module is starting the given process.
 The b10-init module is starting the given process, which will listen on the
 given port number.
 
-% BIND10_STARTING_PROCESS_PORT_ADDRESS starting process %1 (to listen on %2#%3)
+% BIND10_STARTING_PROCESS_PORT_ADDRESS starting process %1 (to listen on %2)
 The b10-init module is starting the given process, which will listen on the
-given address and port number (written as <address>#<port>).
+given address and port number (written as <address>:<port>).
 
 % BIND10_STARTUP_COMPLETE BIND 10 started
 All modules have been successfully started, and BIND 10 is now running.
diff --git a/src/bin/bind10/tests/init_test.py.in b/src/bin/bind10/tests/init_test.py.in
index 9a591ef..f384865 100644
--- a/src/bin/bind10/tests/init_test.py.in
+++ b/src/bin/bind10/tests/init_test.py.in
@@ -2339,6 +2339,16 @@ class SocketSrvTest(unittest.TestCase):
         self.assertEqual({}, self.__b10_init._unix_sockets)
         self.assertTrue(sock.closed)
 
+    def test_log_starting(self):
+        """
+        Checks the log_starting call doesn't raise any errors
+        (does not check actual log output)
+        """
+        self.__b10_init.log_starting("foo")
+        self.__b10_init.log_starting("foo", 1)
+        self.__b10_init.log_starting("foo", 1, "192.0.2.1")
+
+
 class TestFunctions(unittest.TestCase):
     def setUp(self):
         self.lockfile_testpath = \
diff --git a/src/bin/cfgmgr/plugins/datasrc.spec.pre.in b/src/bin/cfgmgr/plugins/datasrc.spec.pre.in
index 6d5bd77..eac14c4 100644
--- a/src/bin/cfgmgr/plugins/datasrc.spec.pre.in
+++ b/src/bin/cfgmgr/plugins/datasrc.spec.pre.in
@@ -18,9 +18,9 @@
                     ],
                     "CH": [
                         {
-                            "type": "static",
-                            "cache-enable": false,
-                            "params": "@@STATIC_ZONE_FILE@@"
+                            "type": "MasterFiles",
+                            "cache-enable": true,
+                            "params": {"BIND": "@@STATIC_ZONE_FILE@@"}
                         }
                     ]
                 },
@@ -63,6 +63,11 @@
                                     "item_optional": false,
                                     "item_default": ""
                                 }
+                            },
+                            {
+                                "item_name": "name",
+                                "item_type": "string",
+                                "item_optional": true
                             }
                         ]
                     }
diff --git a/src/bin/cfgmgr/plugins/tests/Makefile.am b/src/bin/cfgmgr/plugins/tests/Makefile.am
index 9b8b925..1a79e42 100644
--- a/src/bin/cfgmgr/plugins/tests/Makefile.am
+++ b/src/bin/cfgmgr/plugins/tests/Makefile.am
@@ -11,6 +11,8 @@ LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryp
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
+# We need to set B10_FROM_BUILD as some of the tests need to create lock file
+# for logging.
 check-local:
 if ENABLE_PYTHON_COVERAGE
 	touch $(abs_top_srcdir)/.coverage
@@ -20,6 +22,7 @@ endif
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	B10_TEST_PLUGIN_DIR=$(abs_srcdir)/..:$(abs_builddir)/.. \
+	B10_FROM_BUILD=$(abs_top_builddir) \
 	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/cfgmgr:$(abs_top_builddir)/src/lib/dns/python/.libs \
 	$(LIBRARY_PATH_PLACEHOLDER) \
 	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
diff --git a/src/bin/cfgmgr/plugins/tests/datasrc_test.py b/src/bin/cfgmgr/plugins/tests/datasrc_test.py
index 83ec4ca..3c714c6 100644
--- a/src/bin/cfgmgr/plugins/tests/datasrc_test.py
+++ b/src/bin/cfgmgr/plugins/tests/datasrc_test.py
@@ -153,6 +153,101 @@ class DatasrcTest(unittest.TestCase):
             }
         }]})
 
+    def test_names_present(self):
+        """
+        Test we don't choke on configuration with the "name" being present on
+        some items.
+        """
+        self.accept({"IN": [{
+            "type": "MasterFiles",
+            "cache-enable": True,
+            "params": {},
+            "name": "Whatever"
+        }]})
+
+    def test_names_default_classes(self):
+        """
+        Test we can have a client of the same type in different classes
+        without specified name. The defaults should be derived both from
+        the type and the class.
+        """
+        self.accept({
+        "IN": [{
+            "type": "MasterFiles",
+            "cache-enable": True,
+            "params": {}
+        }],
+        "CH": [{
+            "type": "MasterFiles",
+            "cache-enable": True,
+            "params": {}
+        }]})
+
+    def test_names_collision(self):
+        """
+        Reject when two names are the same.
+
+        Cases are:
+        - Explicit names.
+        - Two default names turn out to be the same (same type and class).
+        - One explicit is set to the same as the default one.
+        """
+        self.reject({"IN": [
+        {
+            "type": "MasterFiles",
+            "cache-enable": True,
+            "params": {},
+            "name": "Whatever"
+        },
+        {
+            "type": "MasterFiles",
+            "cache-enable": True,
+            "params": {},
+            "name": "Whatever"
+        }]})
+        # The same, but across different classes is allowed (we would
+        # identify the data source by class+name tuple)
+        self.accept({
+        "IN": [
+            {
+                "type": "MasterFiles",
+                "cache-enable": True,
+                "params": {},
+                "name": "Whatever"
+            }
+        ],
+        "CH": [
+            {
+                "type": "MasterFiles",
+                "cache-enable": True,
+                "params": {},
+                "name": "Whatever"
+            }
+        ]})
+        self.reject({"IN": [
+        {
+            "type": "MasterFiles",
+            "cache-enable": True,
+            "params": {}
+        },
+        {
+            "type": "MasterFiles",
+            "cache-enable": True,
+            "params": {}
+        }]})
+        self.reject({"IN": [
+        {
+            "type": "MasterFiles",
+            "cache-enable": True,
+            "params": {},
+            "name": "MasterFiles"
+        },
+        {
+            "type": "MasterFiles",
+            "cache-enable": True,
+            "params": {}
+        }]})
+
 if __name__ == '__main__':
     isc.log.init("bind10")
     isc.log.resetUnitTestRootLogger()
diff --git a/src/bin/cmdctl/cmdctl.py.in b/src/bin/cmdctl/cmdctl.py.in
index bca0cd9..219dc53 100755
--- a/src/bin/cmdctl/cmdctl.py.in
+++ b/src/bin/cmdctl/cmdctl.py.in
@@ -42,6 +42,7 @@ import random
 import time
 import signal
 from isc.config import ccsession
+import isc.cc.proto_defs
 import isc.util.process
 import isc.net.parse
 from optparse import OptionParser, OptionValueError
@@ -459,7 +460,8 @@ class CommandControl():
                     else:
                         return rcode, {}
                 else:
-                    errstr = str(answer['result'][1])
+                    errstr = \
+                        str(answer[isc.cc.proto_defs.CC_PAYLOAD_RESULT][1])
             except ccsession.ModuleCCSessionError as mcse:
                 errstr = str("Error in ccsession answer:") + str(mcse)
 
diff --git a/src/bin/msgq/Makefile.am b/src/bin/msgq/Makefile.am
index a49b125..185d47a 100644
--- a/src/bin/msgq/Makefile.am
+++ b/src/bin/msgq/Makefile.am
@@ -10,6 +10,7 @@ b10_msgq_DATA = msgq.spec
 CLEANFILES = b10-msgq msgq.pyc
 CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/msgq_messages.py
 CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/msgq_messages.pyc
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/msgq_messages.pyo
 
 man_MANS = b10-msgq.8
 DISTCLEANFILES = $(man_MANS)
diff --git a/src/bin/msgq/msgq.py.in b/src/bin/msgq/msgq.py.in
index 6435c92..8cf6191 100755
--- a/src/bin/msgq/msgq.py.in
+++ b/src/bin/msgq/msgq.py.in
@@ -143,7 +143,7 @@ class SubscriptionManager:
         this group, instance pair.  This includes wildcard subscriptions."""
         target = (group, instance)
         partone = self.find_sub(group, instance)
-        parttwo = self.find_sub(group, "*")
+        parttwo = self.find_sub(group, CC_INSTANCE_WILDCARD)
         return list(set(partone + parttwo))
 
 class MsgQ:
@@ -429,19 +429,19 @@ class MsgQ:
         """Process a single command.  This will split out into one of the
            other functions."""
         logger.debug(TRACE_DETAIL, MSGQ_RECV_HDR, routing)
-        cmd = routing["type"]
-        if cmd == 'send':
+        cmd = routing[CC_HEADER_TYPE]
+        if cmd == CC_COMMAND_SEND:
             self.process_command_send(sock, routing, data)
-        elif cmd == 'subscribe':
+        elif cmd == CC_COMMAND_SUBSCRIBE:
             self.process_command_subscribe(sock, routing, data)
-        elif cmd == 'unsubscribe':
+        elif cmd == CC_COMMAND_UNSUBSCRIBE:
             self.process_command_unsubscribe(sock, routing, data)
-        elif cmd == 'getlname':
+        elif cmd == CC_COMMAND_GET_LNAME:
             self.process_command_getlname(sock, routing, data)
-        elif cmd == 'ping':
+        elif cmd == CC_COMMAND_PING:
             # Command for testing purposes
             self.process_command_ping(sock, routing, data)
-        elif cmd == 'stop':
+        elif cmd == CC_COMMAND_STOP:
             self.stop()
         else:
             logger.error(MSGQ_INVALID_CMD, cmd)
@@ -570,11 +570,12 @@ class MsgQ:
         return "%x_%x@%s" % (time.time(), self.connection_counter, self.hostname)
 
     def process_command_ping(self, sock, routing, data):
-        self.sendmsg(sock, { "type" : "pong" }, data)
+        self.sendmsg(sock, { CC_HEADER_TYPE : CC_COMMAND_PONG }, data)
 
     def process_command_getlname(self, sock, routing, data):
         lname = [ k for k, v in self.lnames.items() if v == sock ][0]
-        self.sendmsg(sock, { "type" : "getlname" }, { "lname" : lname })
+        self.sendmsg(sock, { CC_HEADER_TYPE : CC_COMMAND_GET_LNAME },
+                     { CC_PAYLOAD_LNAME : lname })
 
     def process_command_send(self, sock, routing, data):
         group = routing[CC_HEADER_GROUP]
@@ -638,15 +639,15 @@ class MsgQ:
             self.send_prepared_msg(sock, errmsg)
 
     def process_command_subscribe(self, sock, routing, data):
-        group = routing["group"]
-        instance = routing["instance"]
+        group = routing[CC_HEADER_GROUP]
+        instance = routing[CC_HEADER_INSTANCE]
         if group == None or instance == None:
             return  # ignore invalid packets entirely
         self.subs.subscribe(group, instance, sock)
 
     def process_command_unsubscribe(self, sock, routing, data):
-        group = routing["group"]
-        instance = routing["instance"]
+        group = routing[CC_HEADER_GROUP]
+        instance = routing[CC_HEADER_INSTANCE]
         if group == None or instance == None:
             return  # ignore invalid packets entirely
         self.subs.unsubscribe(group, instance, sock)
diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in
index fd9ac93..82b9dcc 100755
--- a/src/bin/stats/stats_httpd.py.in
+++ b/src/bin/stats/stats_httpd.py.in
@@ -35,6 +35,7 @@ import re
 import isc.cc
 import isc.config
 import isc.util.process
+from isc.util.address_formatter import AddressFormatter
 
 import isc.log
 from isc.log_messages.stats_httpd_messages import *
@@ -325,8 +326,8 @@ class StatsHttpd:
                 server_address, HttpHandler,
                 self.xml_handler, self.xsd_handler, self.xsl_handler,
                 self.write_log)
-            logger.info(STATSHTTPD_STARTED, server_address[0],
-                        server_address[1])
+            logger.info(STATSHTTPD_STARTED,
+                        AddressFormatter(server_address, address_family))
             return httpd
         except (socket.gaierror, socket.error,
                 OverflowError, TypeError) as err:
@@ -341,8 +342,8 @@ class StatsHttpd:
         """Closes sockets for HTTP"""
         while len(self.httpd)>0:
             ht = self.httpd.pop()
-            logger.info(STATSHTTPD_CLOSING, ht.server_address[0],
-                        ht.server_address[1])
+            logger.info(STATSHTTPD_CLOSING,
+                        AddressFormatter(ht.server_address))
             ht.server_close()
 
     def start(self):
diff --git a/src/bin/stats/stats_httpd_messages.mes b/src/bin/stats/stats_httpd_messages.mes
index 93491b6..930a745 100644
--- a/src/bin/stats/stats_httpd_messages.mes
+++ b/src/bin/stats/stats_httpd_messages.mes
@@ -24,7 +24,7 @@ The stats-httpd module was unable to connect to the BIND 10 command
 and control bus. A likely problem is that the message bus daemon
 (b10-msgq) is not running. The stats-httpd module will now shut down.
 
-% STATSHTTPD_CLOSING closing %1#%2
+% STATSHTTPD_CLOSING closing %1
 The stats-httpd daemon will stop listening for requests on the given
 address and port number.
 
@@ -80,7 +80,7 @@ and an error is sent back.
 % STATSHTTPD_SHUTDOWN shutting down
 The stats-httpd daemon is shutting down.
 
-% STATSHTTPD_STARTED listening on %1#%2
+% STATSHTTPD_STARTED listening on %1
 The stats-httpd daemon will now start listening for requests on the
 given address and port number.
 
diff --git a/src/bin/zonemgr/zonemgr.py.in b/src/bin/zonemgr/zonemgr.py.in
index b4e78b1..619e017 100755
--- a/src/bin/zonemgr/zonemgr.py.in
+++ b/src/bin/zonemgr/zonemgr.py.in
@@ -690,7 +690,19 @@ class Zonemgr:
         self.running = True
         try:
             while not self._shutdown_event.is_set():
-                self._module_cc.check_command(False)
+                fileno = self._module_cc.get_socket().fileno()
+                # Wait with select() until there is something to read,
+                # and then read it using a non-blocking read
+                # This may or may not be relevant data for this loop,
+                # but due to the way the zonemgr does threading, we
+                # can't have a blocking read loop here.
+                try:
+                    (reads, _, _) = select.select([fileno], [], [])
+                except select.error as se:
+                    if se.args[0] != errno.EINTR:
+                        raise
+                if fileno in reads:
+                    self._module_cc.check_command(True)
         finally:
             self._module_cc.send_stopping()
 
diff --git a/src/lib/cc/data.h b/src/lib/cc/data.h
index 74e5c12..d0e0d03 100644
--- a/src/lib/cc/data.h
+++ b/src/lib/cc/data.h
@@ -216,7 +216,7 @@ public:
     //@{
     /// Returns the ElementPtr at the given key
     /// \param name The key of the Element to return
-    /// \return The ElementPtr at the given key
+    /// \return The ElementPtr at the given key, or null if not present
     virtual ConstElementPtr get(const std::string& name) const;
 
     /// Sets the ElementPtr at the given key
diff --git a/src/lib/cc/proto_defs.cc b/src/lib/cc/proto_defs.cc
index 72ca41b..8f7fb91 100644
--- a/src/lib/cc/proto_defs.cc
+++ b/src/lib/cc/proto_defs.cc
@@ -34,12 +34,22 @@ const char* const CC_HEADER_WANT_ANSWER = "want_answer";
 const char* const CC_HEADER_REPLY = "reply";
 // The commands in the "type" header
 const char* const CC_COMMAND_SEND = "send";
+const char* const CC_COMMAND_SUBSCRIBE = "subscribe";
+const char* const CC_COMMAND_UNSUBSCRIBE = "unsubscribe";
+const char* const CC_COMMAND_GET_LNAME = "getlname";
+const char* const CC_COMMAND_PING = "ping";
+const char* const CC_COMMAND_PONG = "pong";
+const char* const CC_COMMAND_STOP = "stop";
 // The wildcards of some headers
 const char* const CC_TO_WILDCARD = "*";
 const char* const CC_INSTANCE_WILDCARD = "*";
 // Reply codes
 const int CC_REPLY_NO_RECPT = -1;
 const int CC_REPLY_SUCCESS = 0;
+// Payload in the message
+const char *const CC_PAYLOAD_LNAME = "lname";
+const char *const CC_PAYLOAD_RESULT = "result";
+const char *const CC_PAYLOAD_COMMAND = "command";
 
 }
 }
diff --git a/src/lib/cc/session.cc b/src/lib/cc/session.cc
index f5edcf7..cb1ca39 100644
--- a/src/lib/cc/session.cc
+++ b/src/lib/cc/session.cc
@@ -325,14 +325,14 @@ Session::establish(const char* socket_file) {
     //
     // send a request for our local name, and wait for a response
     //
-    ConstElementPtr get_lname_msg =
-        Element::fromJSON("{ \"type\": \"getlname\" }");
+    ElementPtr get_lname_msg(Element::createMap());
+    get_lname_msg->set(CC_HEADER_TYPE, Element::create(CC_COMMAND_GET_LNAME));
     sendmsg(get_lname_msg);
 
     ConstElementPtr routing, msg;
     recvmsg(routing, msg, false);
 
-    impl_->lname_ = msg->get("lname")->stringValue();
+    impl_->lname_ = msg->get(CC_PAYLOAD_LNAME)->stringValue();
     LOG_DEBUG(logger, DBG_TRACE_DETAILED, CC_LNAME_RECEIVED).arg(impl_->lname_);
 
     // At this point there's no risk of resource leak.
@@ -387,10 +387,10 @@ Session::recvmsg(ConstElementPtr& env, ConstElementPtr& msg,
         for (size_t i = 0; i < impl_->queue_->size(); i++) {
             q_el = impl_->queue_->get(i);
             if (( seq == -1 &&
-                  !q_el->get(0)->contains("reply")
+                  !q_el->get(0)->contains(CC_HEADER_REPLY)
                 ) || (
-                  q_el->get(0)->contains("reply") &&
-                  q_el->get(0)->get("reply")->intValue() == seq
+                  q_el->get(0)->contains(CC_HEADER_REPLY) &&
+                  q_el->get(0)->get(CC_HEADER_REPLY)->intValue() == seq
                 )
                ) {
                    env = q_el->get(0);
@@ -429,10 +429,10 @@ Session::recvmsg(ConstElementPtr& env, ConstElementPtr& msg,
     ConstElementPtr l_msg =
         Element::fromWire(body_wire_stream, length - header_length);
     if ((seq == -1 &&
-         !l_env->contains("reply")
+         !l_env->contains(CC_HEADER_REPLY)
         ) || (
-         l_env->contains("reply") &&
-         l_env->get("reply")->intValue() == seq
+         l_env->contains(CC_HEADER_REPLY) &&
+         l_env->get(CC_HEADER_REPLY)->intValue() == seq
         )
        ) {
         env = l_env;
@@ -453,9 +453,9 @@ Session::subscribe(std::string group, std::string instance) {
     LOG_DEBUG(logger, DBG_TRACE_DETAILED, CC_SUBSCRIBE).arg(group);
     ElementPtr env = Element::createMap();
 
-    env->set("type", Element::create("subscribe"));
-    env->set("group", Element::create(group));
-    env->set("instance", Element::create(instance));
+    env->set(CC_HEADER_TYPE, Element::create(CC_COMMAND_SUBSCRIBE));
+    env->set(CC_HEADER_GROUP, Element::create(group));
+    env->set(CC_HEADER_INSTANCE, Element::create(instance));
 
     sendmsg(env);
 }
@@ -465,9 +465,9 @@ Session::unsubscribe(std::string group, std::string instance) {
     LOG_DEBUG(logger, DBG_TRACE_DETAILED, CC_UNSUBSCRIBE).arg(group);
     ElementPtr env = Element::createMap();
 
-    env->set("type", Element::create("unsubscribe"));
-    env->set("group", Element::create(group));
-    env->set("instance", Element::create(instance));
+    env->set(CC_HEADER_TYPE, Element::create(CC_COMMAND_UNSUBSCRIBE));
+    env->set(CC_HEADER_GROUP, Element::create(group));
+    env->set(CC_HEADER_INSTANCE, Element::create(instance));
 
     sendmsg(env);
 }
@@ -516,13 +516,17 @@ Session::reply(ConstElementPtr envelope, ConstElementPtr newmsg) {
     ElementPtr env = Element::createMap();
     long int nseq = ++impl_->sequence_;
 
-    env->set("type", Element::create("send"));
-    env->set("from", Element::create(impl_->lname_));
-    env->set("to", Element::create(envelope->get("from")->stringValue()));
-    env->set("group", Element::create(envelope->get("group")->stringValue()));
-    env->set("instance", Element::create(envelope->get("instance")->stringValue()));
-    env->set("seq", Element::create(nseq));
-    env->set("reply", Element::create(envelope->get("seq")->intValue()));
+    env->set(CC_HEADER_TYPE, Element::create(CC_COMMAND_SEND));
+    env->set(CC_HEADER_FROM, Element::create(impl_->lname_));
+    env->set(CC_HEADER_TO,
+             Element::create(envelope->get(CC_HEADER_FROM)->stringValue()));
+    env->set(CC_HEADER_GROUP,
+             Element::create(envelope->get(CC_HEADER_GROUP)->stringValue()));
+    env->set(CC_HEADER_INSTANCE,
+             Element::create(envelope->get(CC_HEADER_INSTANCE)->stringValue()));
+    env->set(CC_HEADER_SEQ, Element::create(nseq));
+    env->set(CC_HEADER_REPLY,
+             Element::create(envelope->get(CC_HEADER_SEQ)->intValue()));
 
     sendmsg(env, newmsg);
 
diff --git a/src/lib/config/ccsession.cc b/src/lib/config/ccsession.cc
index 78432ab..f65831d 100644
--- a/src/lib/config/ccsession.cc
+++ b/src/lib/config/ccsession.cc
@@ -57,10 +57,10 @@ namespace config {
 /// Creates a standard config/command protocol answer message
 ConstElementPtr
 createAnswer() {
-    ElementPtr answer = Element::fromJSON("{\"result\": [] }");
+    ElementPtr answer = Element::createMap();
     ElementPtr answer_content = Element::createList();
-    answer_content->add(Element::create(0));
-    answer->set("result", answer_content);
+    answer_content->add(Element::create(isc::cc::CC_REPLY_SUCCESS));
+    answer->set(isc::cc::CC_PAYLOAD_RESULT, answer_content);
 
     return (answer);
 }
@@ -70,22 +70,22 @@ createAnswer(const int rcode, ConstElementPtr arg) {
     if (rcode != 0 && (!arg || arg->getType() != Element::string)) {
         isc_throw(CCSessionError, "Bad or no argument for rcode != 0");
     }
-    ElementPtr answer = Element::fromJSON("{\"result\": [] }");
+    ElementPtr answer = Element::createMap();
     ElementPtr answer_content = Element::createList();
     answer_content->add(Element::create(rcode));
     answer_content->add(arg);
-    answer->set("result", answer_content);
+    answer->set(isc::cc::CC_PAYLOAD_RESULT, answer_content);
 
     return (answer);
 }
 
 ConstElementPtr
 createAnswer(const int rcode, const std::string& arg) {
-    ElementPtr answer = Element::fromJSON("{\"result\": [] }");
+    ElementPtr answer = Element::createMap();
     ElementPtr answer_content = Element::createList();
     answer_content->add(Element::create(rcode));
     answer_content->add(Element::create(arg));
-    answer->set("result", answer_content);
+    answer->set(isc::cc::CC_PAYLOAD_RESULT, answer_content);
 
     return (answer);
 }
@@ -94,8 +94,8 @@ ConstElementPtr
 parseAnswer(int &rcode, ConstElementPtr msg) {
     if (msg &&
         msg->getType() == Element::map &&
-        msg->contains("result")) {
-        ConstElementPtr result = msg->get("result");
+        msg->contains(isc::cc::CC_PAYLOAD_RESULT)) {
+        ConstElementPtr result = msg->get(isc::cc::CC_PAYLOAD_RESULT);
         if (result->getType() != Element::list) {
             isc_throw(CCSessionError, "Result element in answer message is not a list");
         } else if (result->get(0)->getType() != Element::integer) {
@@ -133,7 +133,7 @@ createCommand(const std::string& command, ConstElementPtr arg) {
     if (arg) {
         cmd_parts->add(arg);
     }
-    cmd->set("command", cmd_parts);
+    cmd->set(isc::cc::CC_PAYLOAD_COMMAND, cmd_parts);
     return (cmd);
 }
 
@@ -141,8 +141,8 @@ std::string
 parseCommand(ConstElementPtr& arg, ConstElementPtr command) {
     if (command &&
         command->getType() == Element::map &&
-        command->contains("command")) {
-        ConstElementPtr cmd = command->get("command");
+        command->contains(isc::cc::CC_PAYLOAD_COMMAND)) {
+        ConstElementPtr cmd = command->get(isc::cc::CC_PAYLOAD_COMMAND);
         if (cmd->getType() == Element::list &&
             cmd->size() > 0 &&
             cmd->get(0)->getType() == Element::string) {
@@ -463,10 +463,13 @@ ModuleCCSession::ModuleCCSession(
         isc_throw(CCSessionInitError, answer->str());
     }
 
-    setLocalConfig(Element::fromJSON("{}"));
+    setLocalConfig(Element::createMap());
     // get any stored configuration from the manager
     if (config_handler_) {
-        ConstElementPtr cmd = Element::fromJSON("{ \"command\": [\"get_config\", {\"module_name\":\"" + module_name_ + "\"} ] }");
+        ConstElementPtr cmd =
+            createCommand("get_config",
+                          Element::fromJSON("{\"module_name\":\"" +
+                                            module_name_ + "\"}"));
         seq = session_.group_sendmsg(cmd, "ConfigManager");
         session_.group_recvmsg(env, answer, false, seq);
         ConstElementPtr new_config = parseAnswer(rcode, answer);
@@ -608,14 +611,16 @@ ModuleCCSession::checkCommand() {
 
         /* ignore result messages (in case we're out of sync, to prevent
          * pingpongs */
-        if (data->getType() != Element::map || data->contains("result")) {
+        if (data->getType() != Element::map ||
+            data->contains(isc::cc::CC_PAYLOAD_RESULT)) {
             return (0);
         }
         ConstElementPtr arg;
         ConstElementPtr answer;
         try {
             std::string cmd_str = parseCommand(arg, data);
-            std::string target_module = routing->get("group")->stringValue();
+            std::string target_module =
+                routing->get(isc::cc::CC_HEADER_GROUP)->stringValue();
             if (cmd_str == "config_update") {
                 answer = checkConfigUpdateCommand(target_module, arg);
             } else {
@@ -832,19 +837,19 @@ bool
 ModuleCCSession::requestMatch(const AsyncRecvRequest& request,
                               const ConstElementPtr& envelope) const
 {
-    if (request.is_reply != envelope->contains("reply")) {
+    if (request.is_reply != envelope->contains(isc::cc::CC_HEADER_REPLY)) {
         // Wrong type of message
         return (false);
     }
     if (request.is_reply &&
         (request.seq == -1 ||
-         request.seq == envelope->get("reply")->intValue())) {
+         request.seq == envelope->get(isc::cc::CC_HEADER_REPLY)->intValue())) {
         // This is the correct reply
         return (true);
     }
     if (!request.is_reply &&
-        (request.recipient.empty() ||
-         request.recipient == envelope->get("group")->stringValue())) {
+        (request.recipient.empty() || request.recipient ==
+         envelope->get(isc::cc::CC_HEADER_GROUP)->stringValue())) {
         // This is the correct command
         return (true);
     }
diff --git a/src/lib/config/ccsession.h b/src/lib/config/ccsession.h
index 2f8dd78..bae25bc 100644
--- a/src/lib/config/ccsession.h
+++ b/src/lib/config/ccsession.h
@@ -369,8 +369,8 @@ public:
      */
     int groupSendMsg(isc::data::ConstElementPtr msg,
                      std::string group,
-                     std::string instance = "*",
-                     std::string to = "*",
+                     std::string instance = isc::cc::CC_INSTANCE_WILDCARD,
+                     std::string to = isc::cc::CC_TO_WILDCARD,
                      bool want_answer = false) {
         return (session_.group_sendmsg(msg, group, instance, to, want_answer));
     };
diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am
index abc1e7d..d7c5978 100644
--- a/src/lib/datasrc/Makefile.am
+++ b/src/lib/datasrc/Makefile.am
@@ -39,10 +39,11 @@ libb10_datasrc_la_SOURCES += master_loader_callbacks.h
 libb10_datasrc_la_SOURCES += master_loader_callbacks.cc
 libb10_datasrc_la_SOURCES += rrset_collection_base.h rrset_collection_base.cc
 libb10_datasrc_la_SOURCES += zone_loader.h zone_loader.cc
+libb10_datasrc_la_SOURCES += cache_config.h cache_config.cc
 nodist_libb10_datasrc_la_SOURCES = datasrc_messages.h datasrc_messages.cc
 libb10_datasrc_la_LDFLAGS = -no-undefined -version-info 1:0:1
 
-pkglib_LTLIBRARIES = sqlite3_ds.la static_ds.la
+pkglib_LTLIBRARIES = sqlite3_ds.la
 
 sqlite3_ds_la_SOURCES = sqlite3_accessor.h sqlite3_accessor.cc
 sqlite3_ds_la_SOURCES += sqlite3_accessor_link.cc
@@ -53,12 +54,6 @@ sqlite3_ds_la_LIBADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 sqlite3_ds_la_LIBADD += libb10-datasrc.la
 sqlite3_ds_la_LIBADD += $(SQLITE_LIBS)
 
-static_ds_la_SOURCES = static_datasrc_link.cc
-static_ds_la_SOURCES += static_datasrc.h
-static_ds_la_LDFLAGS = -module -avoid-version
-static_ds_la_LIBADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
-static_ds_la_LIBADD += libb10-datasrc.la
-
 libb10_datasrc_la_LIBADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
 libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/log/libb10-log.la
diff --git a/src/lib/datasrc/cache_config.cc b/src/lib/datasrc/cache_config.cc
new file mode 100644
index 0000000..9cfe3b1
--- /dev/null
+++ b/src/lib/datasrc/cache_config.cc
@@ -0,0 +1,113 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <datasrc/cache_config.h>
+#include <datasrc/client.h>
+#include <datasrc/memory/load_action.h>
+#include <dns/name.h>
+#include <cc/data.h>
+#include <exceptions/exceptions.h>
+
+#include <map>
+#include <string>
+
+using namespace isc::data;
+
+namespace isc {
+namespace datasrc {
+namespace internal {
+
+namespace {
+bool
+getEnabledFromConf(const Element& conf) {
+    return (conf.contains("cache-enable") &&
+            conf.get("cache-enable")->boolValue());
+}
+
+std::string
+getSegmentTypeFromConf(const Element& conf) {
+    // If cache-zones is not explicitly configured, use the default type.
+    // (Ideally we should retrieve the default from the spec).
+    if (!conf.contains("cache-type")) {
+        return ("local");
+    }
+    return (conf.get("cache-type")->stringValue());
+}
+}
+
+CacheConfig::CacheConfig(const std::string& datasrc_type,
+                         const DataSourceClient* datasrc_client,
+                         const Element& datasrc_conf,
+                         bool allowed) :
+    enabled_(allowed && getEnabledFromConf(datasrc_conf)),
+    segment_type_(getSegmentTypeFromConf(datasrc_conf)),
+    datasrc_client_(datasrc_client)
+{
+    ConstElementPtr params = datasrc_conf.get("params");
+    if (!params) {
+        params.reset(new NullElement());
+    }
+    if (datasrc_type == "MasterFiles") {
+        if (datasrc_client_) {
+            isc_throw(InvalidParameter,
+                      "data source client is given for MasterFiles");
+        }
+
+        if (!enabled_) {
+            isc_throw(CacheConfigError,
+                      "The cache must be enabled for the MasterFiles type");
+        }
+
+        typedef std::map<std::string, ConstElementPtr> ZoneToFile;
+        const ZoneToFile& zone_to_file = params->mapValue();
+        ZoneToFile::const_iterator const it_end = zone_to_file.end();
+        for (ZoneToFile::const_iterator it = zone_to_file.begin();
+             it != it_end;
+             ++it)
+        {
+            zone_config_[dns::Name(it->first)] = it->second->stringValue();
+        }
+    } else {
+        if (!datasrc_client_) {
+            isc_throw(InvalidParameter,
+                      "data source client is missing for data source type: "
+                      << datasrc_type);
+        }
+        if (!enabled_) {
+            return;
+        }
+
+        if (!datasrc_conf.contains("cache-zones")) {
+            isc_throw(NotImplemented, "Auto-detection of zones "
+                      "to cache is not yet implemented, supply "
+                      "cache-zones parameter");
+            // TODO: Auto-detect list of all zones in the
+            // data source.
+        }
+
+        const ConstElementPtr zones = datasrc_conf.get("cache-zones");
+        for (size_t i = 0; i < zones->size(); ++i) {
+            const dns::Name zone_name(zones->get(i)->stringValue());
+            if (!zone_config_.insert(Zones::value_type(zone_name,
+                                                       "")).second) {
+                isc_throw(CacheConfigError, "Duplicate cache zone: " <<
+                          zone_name);
+            }
+        }
+    }
+}
+
+} // namespace internal
+} // namespace datasrc
+} // namespace isc
diff --git a/src/lib/datasrc/cache_config.h b/src/lib/datasrc/cache_config.h
new file mode 100644
index 0000000..a615b8a
--- /dev/null
+++ b/src/lib/datasrc/cache_config.h
@@ -0,0 +1,172 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DATASRC_CACHE_CONFIG_H
+#define DATASRC_CACHE_CONFIG_H
+
+#include <exceptions/exceptions.h>
+
+#include <dns/name.h>
+#include <cc/data.h>
+#include <datasrc/memory/load_action.h>
+
+#include <boost/noncopyable.hpp>
+
+#include <map>
+#include <string>
+
+namespace isc {
+namespace datasrc {
+class DataSourceClient;
+
+namespace internal {
+
+/// \brief Exception thrown for configuration error related to in-memory cache.
+class CacheConfigError : public Exception {
+public:
+    CacheConfigError(const char* file, size_t line, const char* what) :
+        Exception(file, line, what)
+    {}
+};
+
+/// \brief Configuration for in-memory cache of a data source.
+///
+/// This class understands and validates the configuration parameters for
+/// \c DataSourceClient related to in-memory cache, and converts it to native,
+/// type-safe objects for the convenience of the user of this class.
+/// Specifically, it allows the user to get the underlying memory segment
+/// type for the cache as a string and to iterate over zone names to be
+/// cached in memory.
+///
+/// It also provides unified interface for getting \c memory::LoadAction
+/// 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.
+/// It's therefore defined in an "internal" namespace, and isn't expected
+/// to be used by other classes or user applications.  Likewise, this file
+/// is not expected to be installed with other publicly usable header files.
+///
+/// It's defined as noncopyable, simply because it's not expected to be
+/// copied in the intended usage for \c ConfigurableClientList.  Prohibiting
+/// copies will help avoid unexpected disruption due to accidental copy and
+/// sharing internal resources as a result of that.
+class CacheConfig : boost::noncopyable {
+public:
+    /// \brief Constructor.
+    ///
+    /// It performs the following validation on the given configuration:
+    /// - For the "MasterFiles" type
+    ///   - datasrc_client_ must not be provided (must be null); throws
+    ///     InvalidParameter otherwise.
+    ///   - cache must be enabled: "cache-enable" configuration item exists
+    ///     and is true, and allowed parameter is true, too; throws
+    ///     CacheConfigError otherwise.
+    ///   - "params" configuration item must be provided and of a map type,
+    ///     and each map entry maps a string to another string; throws
+    ///     data::TypeError otherwise.
+    ///   - the key string of each map entry must be a valid textual
+    ///     representation of a domain name.  Otherwise corresponding
+    ///     exception from the dns::Name class will be thrown.
+    /// - For other types
+    ///   - datasrc_client_ must be provided (must not be null); throws
+    ///     InvalidParameter otherwise.
+    ///   - (Unless cache is disabled) "cache-zones" configuration item must
+    ///     exist and must be a list of strings; throws data::TypeError
+    ///     otherwise.
+    ///   - Each string value of cache-zones entries must be a valid textual
+    ///     representation of a domain name.  Otherwise corresponding
+    ///     exception from the dns::Name class will be thrown.
+    ///   - Names in the list must not have duplicates;
+    ///     throws CacheConfigError otherwise.
+    ///
+    /// For other data source types than "MasterFiles", cache can be disabled.
+    /// In this case cache-zones configuration item is simply ignored, even
+    /// it contains an error that would otherwise trigger an exception.
+    ///
+    /// The specified set of zones (directly in "params" in case of
+    /// "MasterFile", and specified in "cache-zones" for others) can be
+    /// empty.
+    ///
+    /// This constructor also identifies the underlying memory segment type
+    /// used for the cache.  It's given via the "cache-type" configuration
+    /// item if defined; otherwise it defaults to "local".
+    ///
+    /// \throw InvalidParameter Program error at the caller side rather than
+    /// in the configuration (see above)
+    /// \throw CacheConfigError There is a semantics error in the given
+    /// configuration (see above)
+    /// \throw data::TypeError Invalid type of data is found in the
+    /// configuration (see above)
+    /// \throw Other Exceptions from the dns::Name class when conversion from
+    /// text fails (see above)
+    ///
+    /// \param datasrc_type Type of data source. This must be the "type"
+    /// value of the data source configuration.
+    /// \param datasrc_client Client of the underlying data source for the
+    /// cache, if it's used; for MasterFiles types it's null.
+    /// \param datasrc_conf Configuration element for the data source.
+    /// This must be the value of, e.g., data_sources/classes/IN[0] of
+    /// BIND 10 configuration.
+    /// \param allowed Whether in-memory cache is allowed by the process.
+    /// This must be derived from the allow_cache parameter of
+    /// \c ConfigurableClientList::configure().
+    CacheConfig(const std::string& datasrc_type,
+                const DataSourceClient* datasrc_client,
+                const data::Element& datasrc_conf,
+                bool allowed);
+
+    /// \brief Return if the cache is enabled.
+    ///
+    /// The cache is considered enabled iff the "cache-enable" configuration
+    /// item (given on construction) existed and was set to true, and
+    /// the \c allowed parameter to the constructor was true.
+    ///
+    /// \throw None
+    bool isEnabled() const { return (enabled_); }
+
+    /// \brief Return the memory segment type to be used for the zone table.
+    ///
+    /// \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.
+    typedef std::map<dns::Name, std::string>::const_iterator ConstZoneIterator;
+    ConstZoneIterator begin() const { return (zone_config_.begin()); }
+    ConstZoneIterator end() const { return (zone_config_.end()); }
+
+private:
+    const bool enabled_; // if the use of in-memory zone table is enabled
+    const std::string segment_type_;
+    // client of underlying data source, will be NULL for MasterFile datasrc
+    const DataSourceClient* datasrc_client_;
+
+    typedef std::map<dns::Name, std::string> Zones;
+    Zones zone_config_;
+};
+}
+}
+}
+
+#endif  // DATASRC_CACHE_CONFIG_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/datasrc/client_list.cc b/src/lib/datasrc/client_list.cc
index 0750fb6..d366ac3 100644
--- a/src/lib/datasrc/client_list.cc
+++ b/src/lib/datasrc/client_list.cc
@@ -13,20 +13,22 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 
-#include "client_list.h"
-#include "exceptions.h"
-#include "client.h"
-#include "factory.h"
-#include "memory/memory_client.h"
-#include "memory/zone_table_segment.h"
-#include "memory/zone_writer.h"
-#include "memory/zone_data_loader.h"
-#include "memory/zone_data_updater.h"
-#include "logger.h"
+#include <datasrc/client_list.h>
+#include <datasrc/exceptions.h>
+#include <datasrc/client.h>
+#include <datasrc/factory.h>
+#include <datasrc/cache_config.h>
+#include <datasrc/memory/memory_client.h>
+#include <datasrc/memory/zone_table_segment.h>
+#include <datasrc/memory/zone_writer.h>
+#include <datasrc/memory/zone_data_loader.h>
+#include <datasrc/memory/zone_data_updater.h>
+#include <datasrc/logger.h>
 #include <dns/masterload.h>
 #include <util/memory_segment_local.h>
 
 #include <memory>
+#include <set>
 #include <boost/foreach.hpp>
 #include <boost/bind.hpp>
 
@@ -46,25 +48,18 @@ namespace datasrc {
 
 ConfigurableClientList::DataSourceInfo::DataSourceInfo(
     DataSourceClient* data_src_client,
-    const DataSourceClientContainerPtr& container, bool has_cache,
-    const RRClass& rrclass, const shared_ptr<ZoneTableSegment>& segment) :
+    const DataSourceClientContainerPtr& container,
+    boost::shared_ptr<internal::CacheConfig> cache_conf,
+    const RRClass& rrclass, const string& name) :
     data_src_client_(data_src_client),
-    container_(container)
+    container_(container),
+    name_(name),
+    cache_conf_(cache_conf)
 {
-    if (has_cache) {
-        cache_.reset(new InMemoryClient(segment, rrclass));
-        ztable_segment_ = segment;
-    }
-}
-
-ConfigurableClientList::DataSourceInfo::DataSourceInfo(
-    const RRClass& rrclass, const shared_ptr<ZoneTableSegment>& segment,
-    bool has_cache) :
-    data_src_client_(NULL)
-{
-    if (has_cache) {
-        cache_.reset(new InMemoryClient(segment, rrclass));
-        ztable_segment_ = segment;
+    if (cache_conf_ && cache_conf_->isEnabled()) {
+        ztable_segment_.reset(ZoneTableSegment::create(
+                                  rrclass, cache_conf_->getSegmentType()));
+        cache_.reset(new InMemoryClient(ztable_segment_, rrclass));
     }
 }
 
@@ -90,8 +85,7 @@ ConfigurableClientList::configure(const ConstElementPtr& config,
     size_t i(0); // Outside of the try to be able to access it in the catch
     try {
         vector<DataSourceInfo> new_data_sources;
-        shared_ptr<ZoneTableSegment> ztable_segment(
-            ZoneTableSegment::create(*config, rrclass_));
+        set<string> used_names;
         for (; i < config->size(); ++i) {
             // Extract the parameters
             const ConstElementPtr dconf(config->get(i));
@@ -105,51 +99,35 @@ ConfigurableClientList::configure(const ConstElementPtr& config,
             if (paramConf == ConstElementPtr()) {
                 paramConf.reset(new NullElement());
             }
-            const bool want_cache(allow_cache &&
-                                  dconf->contains("cache-enable") &&
-                                  dconf->get("cache-enable")->boolValue());
-
-            if (type == "MasterFiles") {
-                // In case the cache is not allowed, we just skip the master
-                // files (at least for now)
-                if (!allow_cache) {
-                    // We're not going to load these zones. Issue warnings about it.
-                    const map<string, ConstElementPtr>
-                        zones_files(paramConf->mapValue());
-                    for (map<string, ConstElementPtr>::const_iterator
-                         it(zones_files.begin()); it != zones_files.end();
-                         ++it) {
-                        LOG_WARN(logger, DATASRC_LIST_NOT_CACHED).
-                            arg(it->first).arg(rrclass_);
-                    }
-                    continue;
-                }
-                if (!want_cache) {
-                    isc_throw(ConfigurationError, "The cache must be enabled "
-                              "for the MasterFiles type");
-                }
-                new_data_sources.push_back(DataSourceInfo(rrclass_,
-                                                          ztable_segment,
-                                                          true));
-            } else {
-                // Ask the factory to create the data source for us
-                const DataSourcePair ds(this->getDataSourceClient(type,
-                                                                  paramConf));
-                // And put it into the vector
-                new_data_sources.push_back(DataSourceInfo(ds.first, ds.second,
-                                                          want_cache, rrclass_,
-                                                          ztable_segment));
+            // Get the name (either explicit, or guess)
+            const ConstElementPtr name_elem(dconf->get("name"));
+            const string name(name_elem ? name_elem->stringValue() : type);
+            if (!used_names.insert(name).second) {
+                isc_throw(ConfigurationError, "Duplicate name in client list: "
+                          << name);
             }
 
-            if (want_cache) {
-                if (!dconf->contains("cache-zones") && type != "MasterFiles") {
-                    isc_throw(isc::NotImplemented, "Auto-detection of zones "
-                              "to cache is not yet implemented, supply "
-                              "cache-zones parameter");
-                    // TODO: Auto-detect list of all zones in the
-                    // data source.
-                }
+            // Create a client for the underling data source via factory.
+            // If it's our internal type of data source, this is essentially
+            // no-op.  In the latter case, it's of no use unless cache is
+            // allowed; we simply skip building it in that case.
+            const DataSourcePair dsrc_pair = getDataSourceClient(type,
+                                                                 paramConf);
+            if (!allow_cache && !dsrc_pair.first) {
+                LOG_WARN(logger, DATASRC_LIST_NOT_CACHED).
+                    arg(name).arg(rrclass_);
+                continue;
+            }
 
+            boost::shared_ptr<internal::CacheConfig> cache_conf(
+                new internal::CacheConfig(type, dsrc_pair.first, *dconf,
+                                          allow_cache));
+            new_data_sources.push_back(DataSourceInfo(dsrc_pair.first,
+                                                      dsrc_pair.second,
+                                                      cache_conf, rrclass_,
+                                                      name));
+
+            if (cache_conf->isEnabled()) {
                 // List the zones we are loading
                 vector<string> zones_origins;
                 if (type == "MasterFiles") {
@@ -171,6 +149,7 @@ ConfigurableClientList::configure(const ConstElementPtr& config,
                     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);
@@ -214,6 +193,9 @@ ConfigurableClientList::configure(const ConstElementPtr& config,
     } catch (const TypeError& te) {
         isc_throw(ConfigurationError, "Malformed configuration at data source "
                   "no. " << i << ": " << te.what());
+    } catch (const internal::CacheConfigError& ex) {
+        // convert to the "public" exception type.
+        isc_throw(ConfigurationError, ex.what());
     }
 }
 
@@ -457,10 +439,27 @@ ConfigurableClientList::getDataSourceClient(const string& type,
                                             const ConstElementPtr&
                                             configuration)
 {
+    if (type == "MasterFiles") {
+        return (DataSourcePair(0, DataSourceClientContainerPtr()));
+    }
+
     DataSourceClientContainerPtr
         container(new DataSourceClientContainer(type, configuration));
     return (DataSourcePair(&container->getInstance(), container));
 }
 
+vector<DataSourceStatus>
+ConfigurableClientList::getStatus() const {
+    vector<DataSourceStatus> result;
+    BOOST_FOREACH(const DataSourceInfo& info, data_sources_) {
+        // TODO: Once we support mapped cache, decide when we need the
+        // SEGMENT_WAITING.
+        result.push_back(DataSourceStatus(info.name_, info.cache_ ?
+                                          SEGMENT_INUSE : SEGMENT_UNUSED,
+                                          "local"));
+    }
+    return (result);
+}
+
 }
 }
diff --git a/src/lib/datasrc/client_list.h b/src/lib/datasrc/client_list.h
index d1a35b5..cd19c10 100644
--- a/src/lib/datasrc/client_list.h
+++ b/src/lib/datasrc/client_list.h
@@ -46,6 +46,80 @@ class InMemoryClient;
 class ZoneWriter;
 }
 
+namespace internal {
+class CacheConfig;
+}
+
+/// \brief Segment status of the cache
+///
+/// Describes the status in which the memory segment for the in-memory cache of
+// /given data source is.
+enum MemorySegmentState {
+    /// \brief No segment used for this data source.
+    ///
+    /// This is usually a result of the cache being disabled.
+    SEGMENT_UNUSED,
+
+    /// \brief It is a mapped segment and we wait for information how to map
+    ///     it.
+    SEGMENT_WAITING,
+
+    /// \brief The segment is ready to be used.
+    SEGMENT_INUSE
+};
+
+/// \brief Status of one data source.
+///
+/// This indicates the status a data soure is in. It is used with segment
+/// and cache management, to discover the data sources that need external
+/// mapping or local loading.
+///
+/// In future, it may be extended for other purposes, such as performing an
+/// operation on named data source.
+class DataSourceStatus {
+public:
+    /// \brief Constructor
+    ///
+    /// Sets initial values. It doesn't matter what is provided for the type
+    /// if state is SEGMENT_UNUSED, the value is effectively ignored.
+    DataSourceStatus(const std::string& name, MemorySegmentState state,
+                     const std::string& type) :
+        name_(name),
+        type_(type),
+        state_(state)
+    {}
+
+    /// \brief Get the segment state
+    MemorySegmentState getSegmentState() const {
+        return (state_);
+    }
+
+    /// \brief Get the segment type
+    ///
+    /// \note Specific values of the type are only meaningful for the
+    ///     corresponding memory segment implementation and modules that
+    ///     directly manage the segments. Other normal applications should
+    ///     treat them as opaque identifiers.
+    ///
+    /// \throw isc::InvalidOperation if called and state is SEGMENT_UNUSED.
+    const std::string& getSegmentType() const {
+        if (getSegmentState() == SEGMENT_UNUSED) {
+            isc_throw(isc::InvalidOperation,
+                      "No segment used, no type therefore.");
+        }
+        return (type_);
+    }
+
+    /// \brief Get the name.
+    const std::string& getName() const {
+        return (name_);
+    }
+private:
+    std::string name_;
+    std::string type_;
+    MemorySegmentState state_;
+};
+
 /// \brief The list of data source clients.
 ///
 /// The purpose of this class is to hold several data source clients and search
@@ -327,18 +401,11 @@ public:
     ///
     /// \todo The content yet to be defined.
     struct DataSourceInfo {
-        // Plays a role of default constructor too (for vector)
-        DataSourceInfo(const dns::RRClass& rrclass,
-                       const boost::shared_ptr
-                           <isc::datasrc::memory::ZoneTableSegment>&
-                               ztable_segment,
-                       bool has_cache = false);
         DataSourceInfo(DataSourceClient* data_src_client,
                        const DataSourceClientContainerPtr& container,
-                       bool has_cache, const dns::RRClass& rrclass,
-                       const boost::shared_ptr
-                           <isc::datasrc::memory::ZoneTableSegment>&
-                               ztable_segment);
+                       boost::shared_ptr<internal::CacheConfig> cache_conf,
+                       const dns::RRClass& rrclass,
+                       const std::string& name);
         DataSourceClient* data_src_client_;
         DataSourceClientContainerPtr container_;
 
@@ -350,6 +417,11 @@ public:
         const DataSourceClient* getCacheClient() const;
         boost::shared_ptr<memory::InMemoryClient> cache_;
         boost::shared_ptr<memory::ZoneTableSegment> ztable_segment_;
+        std::string name_;
+    private:
+        // this is kept private for now.  When it needs to be accessed,
+        // we'll add a read-only getter method.
+        boost::shared_ptr<internal::CacheConfig> cache_conf_;
     };
 
     /// \brief The collection of data sources.
@@ -369,6 +441,13 @@ public:
     /// Also, derived classes could want to create the data source clients
     /// in a different way, though inheriting this class is not recommended.
     ///
+    /// Some types of data sources can be internal to the \c ClientList
+    /// implementation and do not require a corresponding dynamic module
+    /// loaded via \c DataSourceClientContainer.  In such a case, this method
+    /// simply returns a pair of null pointers.  It will help the caller reduce
+    /// type dependent processing.  Currently, "MasterFiles" is considered to
+    /// be this type of data sources.
+    ///
     /// The parameters are the same as of the constructor.
     /// \return Pair containing both the data source client and the container.
     ///     The container might be NULL in the derived class, it is
@@ -379,6 +458,15 @@ public:
     virtual DataSourcePair getDataSourceClient(const std::string& type,
                                                const data::ConstElementPtr&
                                                configuration);
+
+    /// \brief Get status information of all internal data sources.
+    ///
+    /// Get a DataSourceStatus for current state of each data source client
+    /// in this list.
+    ///
+    /// 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.
     ///
diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes
index 9f57904..46d13a4 100644
--- a/src/lib/datasrc/datasrc_messages.mes
+++ b/src/lib/datasrc/datasrc_messages.mes
@@ -321,12 +321,12 @@ not contain RRs the requested type.  AN NXRRSET indication is returned.
 A debug message indicating that a query for the given name and RR type is being
 processed.
 
-% DATASRC_LIST_NOT_CACHED zone %1/%2 not cached, cache disabled globally. Will not be available.
-The process disabled caching of RR data completely. However, the given zone
-is provided as a master file and it can be served from memory cache only.
-Therefore, the zone will not be available for this process. If this is
-a problem, you should move the zone to some database backend (sqlite3, for
-example) and use it from there.
+% DATASRC_LIST_NOT_CACHED zones in data source %1 for class %2 not cached, cache disabled globally. Will not be available.
+The process disabled caching of RR data completely. However, this data source
+is provided from a master file and it can be served from memory cache only.
+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
diff --git a/src/lib/datasrc/memory/zone_table_segment.cc b/src/lib/datasrc/memory/zone_table_segment.cc
index 50587c4..1253102 100644
--- a/src/lib/datasrc/memory/zone_table_segment.cc
+++ b/src/lib/datasrc/memory/zone_table_segment.cc
@@ -15,6 +15,8 @@
 #include <datasrc/memory/zone_table_segment.h>
 #include <datasrc/memory/zone_table_segment_local.h>
 
+#include <string>
+
 using namespace isc::dns;
 
 namespace isc {
@@ -22,13 +24,15 @@ namespace datasrc {
 namespace memory {
 
 ZoneTableSegment*
-ZoneTableSegment::create(const isc::data::Element&, const RRClass& rrclass) {
-    /// FIXME: For now, we always return ZoneTableSegmentLocal. This
-    /// should be updated eventually to parse the passed Element
-    /// argument and construct a corresponding ZoneTableSegment
-    /// implementation.
-
-    return (new ZoneTableSegmentLocal(rrclass));
+ZoneTableSegment::create(const RRClass& rrclass, const std::string& type) {
+    // This will be a few sequences of if-else and hardcoded.  Not really
+    // sophisticated, but we don't expect to have too many types at the moment.
+    // Until that it becomes a real issue we won't be too smart.
+    if (type == "local") {
+        return (new ZoneTableSegmentLocal(rrclass));
+    }
+    isc_throw(UnknownSegmentType, "Zone table segment type not supported: "
+              << type);
 }
 
 void
diff --git a/src/lib/datasrc/memory/zone_table_segment.h b/src/lib/datasrc/memory/zone_table_segment.h
index 88e69f6..1440a2e 100644
--- a/src/lib/datasrc/memory/zone_table_segment.h
+++ b/src/lib/datasrc/memory/zone_table_segment.h
@@ -15,15 +15,20 @@
 #ifndef ZONE_TABLE_SEGMENT_H
 #define ZONE_TABLE_SEGMENT_H
 
+#include <exceptions/exceptions.h>
+
 #include <dns/rrclass.h>
+
 #include <datasrc/memory/zone_table.h>
-#include "load_action.h"
+#include <datasrc/memory/load_action.h>
+
 #include <cc/data.h>
 #include <util/memory_segment.h>
 
 #include <boost/interprocess/offset_ptr.hpp>
 
-#include <stdlib.h>
+#include <cstdlib>
+#include <string>
 
 namespace isc {
 // Some forward declarations
@@ -35,6 +40,15 @@ namespace datasrc {
 namespace memory {
 class ZoneWriter;
 
+/// \brief Exception thrown when unknown or unsupported type of zone table
+/// segment is specified.
+class UnknownSegmentType : public Exception {
+public:
+    UnknownSegmentType(const char* file, size_t line, const char* what) :
+        Exception(file, line, what)
+    {}
+};
+
 /// \brief Memory-management independent entry point that contains a
 /// pointer to a zone table in memory.
 ///
@@ -97,26 +111,14 @@ public:
     /// dynamically-allocated object. The caller is responsible for
     /// destroying it with \c ZoneTableSegment::destroy().
     ///
-    /// FIXME: For now, we always return ZoneTableSegmentLocal
-    /// regardless of the passed \c config.
+    /// \throw UnknownSegmentType The memory segment type specified in
+    /// \c config is not known or not supported in this implementation.
     ///
-    /// \param config The configuration based on which a derived object
-    ///               is returned.
-    /// \return Returns a ZoneTableSegment object
-    static ZoneTableSegment* create(const isc::data::Element& config,
-                                    const isc::dns::RRClass& rrclass);
-
-    /// \brief Temporary/Testing version of create.
-    ///
-    /// This exists as a temporary solution during the migration phase
-    /// towards using the ZoneTableSegment. It doesn't take a config,
-    /// but a memory segment instead. If you can, you should use the
-    /// other version, this one will be gone soon.
-    ///
-    /// \param segment The memory segment to use.
-    /// \return Returns a new ZoneTableSegment object.
-    /// \todo Remove this method.
-    static ZoneTableSegment* create(isc::util::MemorySegment& segment);
+    /// \param rrclass The RR class of the zones to be maintained in the table.
+    /// \param type The memory segment type used for the zone table segment.
+    /// \return Returns a ZoneTableSegment object of the specified type.
+    static ZoneTableSegment* create(const isc::dns::RRClass& rrclass,
+                                    const std::string& type);
 
     /// \brief Destroy a ZoneTableSegment
     ///
@@ -147,3 +149,7 @@ public:
 } // namespace isc
 
 #endif // ZONE_TABLE_SEGMENT_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/datasrc/static_datasrc.h b/src/lib/datasrc/static_datasrc.h
deleted file mode 100644
index d5d8875..0000000
--- a/src/lib/datasrc/static_datasrc.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-
-#ifndef DATASRC_STATIC_H
-#define DATASRC_STATIC_H
-
-#include <datasrc/database.h>
-#include <cc/data.h>
-
-#include <string>
-
-namespace isc {
-namespace datasrc {
-
-/// \brief Creates an instance of the static datasource client
-///
-/// Currently the configuration passed here must be a StringElement,
-/// containing the path to a zone file for the BIND./CH zone.
-///
-/// \param config The configuration for the datasource instance (see above)
-/// \param error This string will be set to an error message if an error occurs
-///              during initialization
-/// \return An instance of the static datasource client, or NULL if there was
-///         an error
-extern "C" DataSourceClient* createInstance(isc::data::ConstElementPtr config,
-                                            std::string& error);
-
-/// \brief Destroy the instance created by createInstance()
-extern "C" void destroyInstance(DataSourceClient* instance);
-
-}
-}
-
-#endif  // DATASRC_STATIC_H
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/lib/datasrc/static_datasrc_link.cc b/src/lib/datasrc/static_datasrc_link.cc
deleted file mode 100644
index 1b767a4..0000000
--- a/src/lib/datasrc/static_datasrc_link.cc
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include "client.h"
-#include "static_datasrc.h"
-#include <datasrc/memory/memory_client.h>
-#include <datasrc/memory/zone_table_segment.h>
-
-#include <cc/data.h>
-#include <dns/rrclass.h>
-
-#include <memory>
-#include <exception>
-
-using namespace isc::data;
-using namespace isc::dns;
-using namespace boost;
-using namespace std;
-
-namespace isc {
-namespace datasrc {
-
-DataSourceClient*
-createInstance(ConstElementPtr config, string& error) {
-    try {
-        // FIXME: Fix the config that should be passed to
-        // ZoneTableSegment::create() when it actually uses the config
-        // to do something.
-        shared_ptr<memory::ZoneTableSegment> ztable_segment(
-            memory::ZoneTableSegment::create(isc::data::NullElement(),
-                                             RRClass::CH()));
-        // Create the data source
-        auto_ptr<memory::InMemoryClient> client
-            (new memory::InMemoryClient(ztable_segment, RRClass::CH()));
-
-        // Fill it with data
-        const string path(config->stringValue());
-        client->load(Name("BIND"), path);
-
-        return (client.release());
-    }
-    catch (const std::exception& e) {
-        error = e.what();
-    }
-    catch (...) {
-        error = "Unknown exception";
-    }
-    return (NULL);
-}
-
-void
-destroyInstance(DataSourceClient* instance) {
-    delete instance;
-}
-
-}
-}
diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am
index fa1f01c..341d6eb 100644
--- a/src/lib/datasrc/tests/Makefile.am
+++ b/src/lib/datasrc/tests/Makefile.am
@@ -48,6 +48,7 @@ common_ldadd += $(GTEST_LDADD) $(SQLITE_LIBS)
 run_unittests_SOURCES = $(common_sources)
 
 run_unittests_SOURCES += test_client.h test_client.cc
+run_unittests_SOURCES += mock_client.h mock_client.cc
 run_unittests_SOURCES += logger_unittest.cc
 run_unittests_SOURCES += client_unittest.cc
 run_unittests_SOURCES += database_unittest.h database_unittest.cc
@@ -58,6 +59,7 @@ run_unittests_SOURCES += faked_nsec3.h faked_nsec3.cc
 run_unittests_SOURCES += client_list_unittest.cc
 run_unittests_SOURCES += master_loader_callbacks_test.cc
 run_unittests_SOURCES += zone_loader_unittest.cc
+run_unittests_SOURCES += cache_config_unittest.cc
 
 # We need the actual module implementation in the tests (they are not part
 # of libdatasrc)
diff --git a/src/lib/datasrc/tests/cache_config_unittest.cc b/src/lib/datasrc/tests/cache_config_unittest.cc
new file mode 100644
index 0000000..8b3e6af
--- /dev/null
+++ b/src/lib/datasrc/tests/cache_config_unittest.cc
@@ -0,0 +1,241 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <datasrc/cache_config.h>
+#include <datasrc/tests/mock_client.h>
+
+#include <cc/data.h>
+#include <dns/name.h>
+
+#include <gtest/gtest.h>
+
+#include <iterator>             // for std::distance
+
+using namespace isc::datasrc;
+using namespace isc::data;
+using namespace isc::dns;
+using isc::datasrc::unittest::MockDataSourceClient;
+using isc::datasrc::internal::CacheConfig;
+using isc::datasrc::internal::CacheConfigError;
+
+namespace {
+
+const char* zones[] = {
+    "example.org.",
+    "example.com.",
+    NULL
+};
+
+class CacheConfigTest : public ::testing::Test {
+protected:
+    CacheConfigTest() :
+        mock_client_(zones),
+        master_config_(Element::fromJSON(
+                           "{\"cache-enable\": true,"
+                           " \"params\": "
+                           "  {\".\": \"" TEST_DATA_DIR "/root.zone\"}"
+                           "}")),
+        mock_config_(Element::fromJSON("{\"cache-enable\": true,"
+                                       " \"cache-zones\": [\".\"]}"))
+    {}
+
+    MockDataSourceClient mock_client_;
+    const ConstElementPtr master_config_; // valid config for MasterFiles
+    const ConstElementPtr mock_config_; // valid config for MasterFiles
+};
+
+size_t
+countZones(const CacheConfig& cache_config) {
+    return (std::distance(cache_config.begin(), cache_config.end()));
+}
+
+TEST_F(CacheConfigTest, constructMasterFiles) {
+    // A simple case: configuring a MasterFiles table with a single zone
+    const CacheConfig cache_conf("MasterFiles", 0, *master_config_, true);
+    EXPECT_EQ(1, countZones(cache_conf));
+
+    // With multiple zones.  Note that the constructor doesn't check if the
+    // file exists, so they can be anything.
+    const ConstElementPtr config_elem_multi(
+        Element::fromJSON("{\"cache-enable\": true,"
+                          " \"params\": "
+                          "{\"example.com\": \"file1\","
+                          " \"example.org\": \"file2\","
+                          " \"example.info\": \"file3\"}"
+                          "}"));
+    EXPECT_EQ(3, countZones(CacheConfig("MasterFiles", 0, *config_elem_multi,
+                                        true)));
+
+    // A bit unusual, but acceptable case: empty parameters, so no zones.
+    EXPECT_EQ(0, countZones(
+                  CacheConfig("MasterFiles", 0,
+                              *Element::fromJSON("{\"cache-enable\": true,"
+                                                 " \"params\": {}}"), true)));
+}
+
+TEST_F(CacheConfigTest, badConstructMasterFiles) {
+    // no "params"
+    EXPECT_THROW(CacheConfig("MasterFiles", 0,
+                             *Element::fromJSON("{\"cache-enable\": true}"),
+                             true),
+                 isc::data::TypeError);
+
+    // no "cache-enable"
+    EXPECT_THROW(CacheConfig("MasterFiles", 0,
+                             *Element::fromJSON("{\"params\": {}}"), true),
+                 CacheConfigError);
+    // cache disabled for MasterFiles
+    EXPECT_THROW(CacheConfig("MasterFiles", 0,
+                             *Element::fromJSON("{\"cache-enable\": false,"
+                                                " \"params\": {}}"), true),
+                 CacheConfigError);
+    // cache enabled but not "allowed"
+    EXPECT_THROW(CacheConfig("MasterFiles", 0,
+                             *Element::fromJSON("{\"cache-enable\": false,"
+                                                " \"params\": {}}"), false),
+                 CacheConfigError);
+    // type error for cache-enable
+    EXPECT_THROW(CacheConfig("MasterFiles", 0,
+                             *Element::fromJSON("{\"cache-enable\": 1,"
+                                                " \"params\": {}}"), true),
+                 isc::data::TypeError);
+
+    // "params" is not a map
+    EXPECT_THROW(CacheConfig("MasterFiles", 0,
+                             *Element::fromJSON("{\"cache-enable\": true,"
+                                                " \"params\": []}"), true),
+                 isc::data::TypeError);
+
+    // bogus zone name
+    const ConstElementPtr bad_config(Element::fromJSON(
+                                         "{\"cache-enable\": true,"
+                                         " \"params\": "
+                                         "{\"bad..name\": \"file1\"}}"));
+    EXPECT_THROW(CacheConfig("MasterFiles", 0, *bad_config, true),
+                 isc::dns::EmptyLabel);
+
+    // file name is not a string
+    const ConstElementPtr bad_config2(Element::fromJSON(
+                                          "{\"cache-enable\": true,"
+                                          " \"params\": {\".\": 1}}"));
+    EXPECT_THROW(CacheConfig("MasterFiles", 0, *bad_config2, true),
+                 isc::data::TypeError);
+
+    // Specify data source client (must be null for MasterFiles)
+    EXPECT_THROW(CacheConfig("MasterFiles", &mock_client_,
+                             *Element::fromJSON("{\"cache-enable\": true,"
+                                                " \"params\": {}}"), true),
+                 isc::InvalidParameter);
+}
+
+TEST_F(CacheConfigTest, constructWithMock) {
+    // Performing equivalent set of tests as constructMasterFiles
+
+    // Configure with a single zone.
+    const CacheConfig cache_conf("mock", &mock_client_, *mock_config_, true);
+    EXPECT_EQ(1, countZones(cache_conf));
+    EXPECT_TRUE(cache_conf.isEnabled());
+
+    // Configure with multiple zones.
+    const ConstElementPtr config_elem_multi(
+        Element::fromJSON("{\"cache-enable\": true,"
+                          " \"cache-zones\": "
+                          "[\"example.com\", \"example.org\",\"example.info\"]"
+                          "}"));
+    EXPECT_EQ(3, countZones(CacheConfig("mock", &mock_client_,
+                                        *config_elem_multi, true)));
+
+    // Empty
+    EXPECT_EQ(0, countZones(
+                  CacheConfig("mock", &mock_client_,
+                              *Element::fromJSON("{\"cache-enable\": true,"
+                                                 " \"cache-zones\": []}"),
+                              true)));
+
+    // disabled.  value of cache-zones are ignored.
+    const ConstElementPtr config_elem_disabled(
+        Element::fromJSON("{\"cache-enable\": false,"
+                          " \"cache-zones\": [\"example.com\"]}"));
+    EXPECT_FALSE(CacheConfig("mock", &mock_client_, *config_elem_disabled,
+                             true).isEnabled());
+    // enabled but not "allowed".  same effect.
+    EXPECT_FALSE(CacheConfig("mock", &mock_client_,
+                             *Element::fromJSON("{\"cache-enable\": true,"
+                                                " \"cache-zones\": []}"),
+                             false).isEnabled());
+}
+
+TEST_F(CacheConfigTest, badConstructWithMock) {
+    // no "cache-zones" (may become valid in future, but for now "notimp")
+    EXPECT_THROW(CacheConfig("mock", &mock_client_,
+                             *Element::fromJSON("{\"cache-enable\": true}"),
+                             true),
+                 isc::NotImplemented);
+
+    // "cache-zones" is not a list
+    EXPECT_THROW(CacheConfig("mock", &mock_client_,
+                             *Element::fromJSON("{\"cache-enable\": true,"
+                                                " \"cache-zones\": {}}"),
+                             true),
+                 isc::data::TypeError);
+
+    // "cache-zone" entry is not a string
+    EXPECT_THROW(CacheConfig("mock", &mock_client_,
+                             *Element::fromJSON("{\"cache-enable\": true,"
+                                                " \"cache-zones\": [1]}"),
+                             true),
+                 isc::data::TypeError);
+
+    // bogus zone name
+    const ConstElementPtr bad_config(Element::fromJSON(
+                                         "{\"cache-enable\": true,"
+                                         " \"cache-zones\": [\"bad..\"]}"));
+    EXPECT_THROW(CacheConfig("mock", &mock_client_, *bad_config, true),
+                 isc::dns::EmptyLabel);
+
+    // duplicate zone name (note that comparison is case insensitive)
+    const ConstElementPtr dup_config(Element::fromJSON(
+                                         "{\"cache-enable\": true,"
+                                         " \"cache-zones\": "
+                                         " [\"example\", \"EXAMPLE\"]}"));
+    EXPECT_THROW(CacheConfig("mock", &mock_client_, *dup_config, true),
+                 CacheConfigError);
+
+    // datasrc is null
+    EXPECT_THROW(CacheConfig("mock", 0, *mock_config_, true),
+                 isc::InvalidParameter);
+}
+
+TEST_F(CacheConfigTest, getSegmentType) {
+    // Default type
+    EXPECT_EQ("local",
+              CacheConfig("MasterFiles", 0,
+                          *master_config_, true).getSegmentType());
+
+    // If we explicitly configure it, that value should be used.
+    ConstElementPtr config(Element::fromJSON("{\"cache-enable\": true,"
+                                             " \"cache-type\": \"mapped\","
+                                             " \"params\": {}}" ));
+    EXPECT_EQ("mapped",
+              CacheConfig("MasterFiles", 0, *config, true).getSegmentType());
+
+    // Wrong types: should be rejected at construction time
+    ConstElementPtr badconfig(Element::fromJSON("{\"cache-enable\": true,"
+                                                " \"cache-type\": 1,"
+                                                " \"params\": {}}"));
+    EXPECT_THROW(CacheConfig("MasterFiles", 0, *badconfig, true),
+                 isc::data::TypeError);
+}
+
+}
diff --git a/src/lib/datasrc/tests/client_list_unittest.cc b/src/lib/datasrc/tests/client_list_unittest.cc
index 3d78138..ab553cb 100644
--- a/src/lib/datasrc/tests/client_list_unittest.cc
+++ b/src/lib/datasrc/tests/client_list_unittest.cc
@@ -21,6 +21,8 @@
 #include <datasrc/memory/zone_finder.h>
 #include <datasrc/memory/zone_writer.h>
 
+#include <datasrc/tests/mock_client.h>
+
 #include <dns/rrclass.h>
 #include <dns/rrttl.h>
 #include <dns/rdataclass.h>
@@ -33,6 +35,7 @@
 #include <fstream>
 
 using namespace isc::datasrc;
+using isc::datasrc::unittest::MockDataSourceClient;
 using isc::datasrc::memory::InMemoryClient;
 using isc::datasrc::memory::ZoneTableSegment;
 using isc::datasrc::memory::InMemoryZoneFinder;
@@ -45,162 +48,6 @@ using namespace std;
 
 namespace {
 
-// A test data source. It pretends it has some zones.
-class MockDataSourceClient : public DataSourceClient {
-public:
-    class Finder : public ZoneFinder {
-    public:
-        Finder(const Name& origin) :
-            origin_(origin)
-        {}
-        Name getOrigin() const { return (origin_); }
-        // The rest is not to be called, so just have them
-        RRClass getClass() const {
-            isc_throw(isc::NotImplemented, "Not implemented");
-        }
-        shared_ptr<Context> find(const Name&, const RRType&,
-                                 const FindOptions)
-        {
-            isc_throw(isc::NotImplemented, "Not implemented");
-        }
-        shared_ptr<Context> findAll(const Name&,
-                                    vector<ConstRRsetPtr>&,
-                                    const FindOptions)
-        {
-            isc_throw(isc::NotImplemented, "Not implemented");
-        }
-        FindNSEC3Result findNSEC3(const Name&, bool) {
-            isc_throw(isc::NotImplemented, "Not implemented");
-        }
-    private:
-        Name origin_;
-    };
-    class Iterator : public ZoneIterator {
-    public:
-        Iterator(const Name& origin, bool include_a) :
-            origin_(origin),
-            soa_(new RRset(origin_, RRClass::IN(), RRType::SOA(),
-                           RRTTL(3600)))
-        {
-            // The RData here is bogus, but it is not used to anything. There
-            // just needs to be some.
-            soa_->addRdata(rdata::generic::SOA(Name::ROOT_NAME(),
-                                               Name::ROOT_NAME(),
-                                               0, 0, 0, 0, 0));
-            rrsets_.push_back(soa_);
-
-            RRsetPtr rrset(new RRset(origin_, RRClass::IN(), RRType::NS(),
-                                     RRTTL(3600)));
-            rrset->addRdata(rdata::generic::NS(Name::ROOT_NAME()));
-            rrsets_.push_back(rrset);
-
-            if (include_a) {
-                 // Dummy A rrset. This is used for checking zone data
-                 // after reload.
-                 rrset.reset(new RRset(Name("tstzonedata").concatenate(origin_),
-                                       RRClass::IN(), RRType::A(),
-                                       RRTTL(3600)));
-                 rrset->addRdata(rdata::in::A("192.0.2.1"));
-                 rrsets_.push_back(rrset);
-            }
-
-            rrsets_.push_back(ConstRRsetPtr());
-
-            it_ = rrsets_.begin();
-        }
-        virtual isc::dns::ConstRRsetPtr getNextRRset() {
-            ConstRRsetPtr result = *it_;
-            ++it_;
-            return (result);
-        }
-        virtual isc::dns::ConstRRsetPtr getSOA() const {
-            return (soa_);
-        }
-    private:
-        const Name origin_;
-        const RRsetPtr soa_;
-        std::vector<ConstRRsetPtr> rrsets_;
-        std::vector<ConstRRsetPtr>::const_iterator it_;
-    };
-    // Constructor from a list of zones.
-    MockDataSourceClient(const char* zone_names[]) :
-        have_a_(true), use_baditerator_(true)
-    {
-        for (const char** zone(zone_names); *zone; ++zone) {
-            zones.insert(Name(*zone));
-        }
-    }
-    // Constructor from configuration. The list of zones will be empty, but
-    // it will keep the configuration inside for further inspection.
-    MockDataSourceClient(const string& type,
-                         const ConstElementPtr& configuration) :
-        type_(type),
-        configuration_(configuration),
-        have_a_(true), use_baditerator_(true)
-    {
-        EXPECT_NE("MasterFiles", type) << "MasterFiles is a special case "
-            "and it never should be created as a data source client";
-        if (configuration_->getType() == Element::list) {
-            for (size_t i(0); i < configuration_->size(); ++i) {
-                zones.insert(Name(configuration_->get(i)->stringValue()));
-            }
-        }
-    }
-    virtual FindResult findZone(const Name& name) const {
-        if (zones.empty()) {
-            return (FindResult(result::NOTFOUND, ZoneFinderPtr()));
-        }
-        set<Name>::const_iterator it(zones.upper_bound(name));
-        if (it == zones.begin()) {
-            return (FindResult(result::NOTFOUND, ZoneFinderPtr()));
-        }
-        --it;
-        NameComparisonResult compar(it->compare(name));
-        const ZoneFinderPtr finder(new Finder(*it));
-        switch (compar.getRelation()) {
-            case NameComparisonResult::EQUAL:
-                return (FindResult(result::SUCCESS, finder));
-            case NameComparisonResult::SUPERDOMAIN:
-                return (FindResult(result::PARTIALMATCH, finder));
-            default:
-                return (FindResult(result::NOTFOUND, ZoneFinderPtr()));
-        }
-    }
-    // These methods are not used. They just need to be there to have
-    // complete vtable.
-    virtual ZoneUpdaterPtr getUpdater(const Name&, bool, bool) const {
-        isc_throw(isc::NotImplemented, "Not implemented");
-    }
-    virtual pair<ZoneJournalReader::Result, ZoneJournalReaderPtr>
-        getJournalReader(const Name&, uint32_t, uint32_t) const
-    {
-        isc_throw(isc::NotImplemented, "Not implemented");
-    }
-    virtual ZoneIteratorPtr getIterator(const Name& name, bool) const {
-        if (use_baditerator_ && name == Name("noiter.org")) {
-            isc_throw(isc::NotImplemented, "Asked not to be implemented");
-        } else if (use_baditerator_ && name == Name("null.org")) {
-            return (ZoneIteratorPtr());
-        } else {
-            FindResult result(findZone(name));
-            if (result.code == isc::datasrc::result::SUCCESS) {
-                return (ZoneIteratorPtr(new Iterator(name, have_a_)));
-            } else {
-                isc_throw(DataSourceError, "No such zone");
-            }
-        }
-    }
-    void disableA() { have_a_ = false; }
-    void disableBadIterator() { use_baditerator_ = false; }
-    const string type_;
-    const ConstElementPtr configuration_;
-private:
-    set<Name> zones;
-    bool have_a_; // control the iterator behavior whether to include A record
-    bool use_baditerator_; // whether to use bogus zone iterators for tests
-};
-
-
 // The test version is the same as the normal version. We, however, add
 // some methods to dig directly in the internals, for the tests.
 class TestedList : public ConfigurableClientList {
@@ -219,6 +66,9 @@ public:
         if (type == "error") {
             isc_throw(DataSourceError, "The error data source type");
         }
+        if (type == "MasterFiles") {
+            return (DataSourcePair(0, DataSourceClientContainerPtr()));
+        }
         shared_ptr<MockDataSourceClient>
             ds(new MockDataSourceClient(type, configuration));
         // Make sure it is deleted when the test list is deleted.
@@ -269,8 +119,7 @@ public:
             "   \"params\": [\"example.org\", \"example.com\", "
             "                \"noiter.org\", \"null.org\"]"
             "}]")),
-        config_(Element::fromJSON("{}")),
-        ztable_segment_(ZoneTableSegment::create(*config_, rrclass_))
+        ztable_segment_(ZoneTableSegment::create(rrclass_, "local"))
     {
         for (size_t i(0); i < ds_count; ++ i) {
             shared_ptr<MockDataSourceClient>
@@ -278,7 +127,8 @@ public:
             ds_.push_back(ds);
             ds_info_.push_back(ConfigurableClientList::DataSourceInfo(
                                    ds.get(), DataSourceClientContainerPtr(),
-                                   false, rrclass_, ztable_segment_));
+                                   boost::shared_ptr<internal::CacheConfig>(),
+                                   rrclass_, ""));
         }
     }
 
@@ -382,7 +232,7 @@ public:
     const ClientList::FindResult negative_result_;
     vector<shared_ptr<MockDataSourceClient> > ds_;
     vector<ConfigurableClientList::DataSourceInfo> ds_info_;
-    const ConstElementPtr config_elem_, config_elem_zones_, config_;
+    const ConstElementPtr config_elem_, config_elem_zones_;
     shared_ptr<ZoneTableSegment> ztable_segment_;
 };
 
@@ -512,12 +362,12 @@ TEST_F(ListTest, configureMulti) {
     const ConstElementPtr elem(Element::fromJSON("["
         "{"
         "   \"type\": \"type1\","
-        "   \"cache\": \"off\","
+        "   \"cache-enable\": false,"
         "   \"params\": {}"
         "},"
         "{"
         "   \"type\": \"type2\","
-        "   \"cache\": \"off\","
+        "   \"cache-enable\": false,"
         "   \"params\": {}"
         "}]"
     ));
@@ -546,7 +396,7 @@ TEST_F(ListTest, configureParams) {
         ConstElementPtr elem(Element::fromJSON(string("["
             "{"
             "   \"type\": \"t\","
-            "   \"cache\": \"off\","
+            "   \"cache-enable\": false,"
             "   \"params\": ") + *param +
             "}]"));
         list_->configure(elem, true);
@@ -555,6 +405,33 @@ TEST_F(ListTest, configureParams) {
     }
 }
 
+TEST_F(ListTest, status) {
+    EXPECT_TRUE(list_->getStatus().empty());
+    const ConstElementPtr elem(Element::fromJSON("["
+        "{"
+        "   \"type\": \"type1\","
+        "   \"cache-enable\": false,"
+        "   \"params\": {}"
+        "},"
+        "{"
+        "   \"type\": \"type2\","
+        "   \"cache-enable\": true,"
+        "   \"cache-zones\": [],"
+        "   \"name\": \"Test name\","
+        "   \"params\": {}"
+        "}]"
+    ));
+    list_->configure(elem, true);
+    const vector<DataSourceStatus> statuses(list_->getStatus());
+    ASSERT_EQ(2, statuses.size());
+    EXPECT_EQ("type1", statuses[0].getName());
+    EXPECT_EQ(SEGMENT_UNUSED, statuses[0].getSegmentState());
+    EXPECT_THROW(statuses[0].getSegmentType(), isc::InvalidOperation);
+    EXPECT_EQ("Test name", statuses[1].getName());
+    EXPECT_EQ(SEGMENT_INUSE, statuses[1].getSegmentState());
+    EXPECT_EQ("local", statuses[1].getSegmentType());
+}
+
 TEST_F(ListTest, wrongConfig) {
     const char* configs[] = {
         // A lot of stuff missing from there
@@ -834,6 +711,54 @@ TEST_F(ListTest, masterFiles) {
     EXPECT_EQ(0, list_->getDataSources().size());
 }
 
+// Test the names are set correctly and collission is detected.
+TEST_F(ListTest, names) {
+    // Explicit name
+    const ConstElementPtr elem1(Element::fromJSON("["
+        "{"
+        "   \"type\": \"MasterFiles\","
+        "   \"cache-enable\": true,"
+        "   \"params\": {"
+        "       \".\": \"" TEST_DATA_DIR "/root.zone\""
+        "   },"
+        "   \"name\": \"Whatever\""
+        "}]"));
+    list_->configure(elem1, true);
+    EXPECT_EQ("Whatever", list_->getDataSources()[0].name_);
+
+    // Default name
+    const ConstElementPtr elem2(Element::fromJSON("["
+        "{"
+        "   \"type\": \"MasterFiles\","
+        "   \"cache-enable\": true,"
+        "   \"params\": {"
+        "       \".\": \"" TEST_DATA_DIR "/root.zone\""
+        "   }"
+        "}]"));
+    list_->configure(elem2, true);
+    EXPECT_EQ("MasterFiles", list_->getDataSources()[0].name_);
+
+    // Collission
+    const ConstElementPtr elem3(Element::fromJSON("["
+        "{"
+        "   \"type\": \"MasterFiles\","
+        "   \"cache-enable\": true,"
+        "   \"params\": {"
+        "       \".\": \"" TEST_DATA_DIR "/root.zone\""
+        "   }"
+        "},"
+        "{"
+        "   \"type\": \"MasterFiles\","
+        "   \"cache-enable\": true,"
+        "   \"params\": {"
+        "       \".\": \"" TEST_DATA_DIR "/root.zone\""
+        "   },"
+        "   \"name\": \"MasterFiles\""
+        "}]"));
+    EXPECT_THROW(list_->configure(elem3, true),
+                 ConfigurableClientList::ConfigurationError);
+}
+
 TEST_F(ListTest, BadMasterFile) {
     // Configuration should succeed, and the good zones in the list
     // below should be loaded. No bad zones should be loaded.
@@ -1088,4 +1013,16 @@ TYPED_TEST(ReloadTest, reloadMasterFile) {
                                                          RRType::TXT())->code);
 }
 
+// Check the status holds data
+TEST(DataSourceStatus, status) {
+    const DataSourceStatus status("Test", SEGMENT_INUSE, "local");
+    EXPECT_EQ("Test", status.getName());
+    EXPECT_EQ(SEGMENT_INUSE, status.getSegmentState());
+    EXPECT_EQ("local", status.getSegmentType());
+    const DataSourceStatus status_unused("Unused", SEGMENT_UNUSED, "");
+    EXPECT_EQ("Unused", status_unused.getName());
+    EXPECT_EQ(SEGMENT_UNUSED, status_unused.getSegmentState());
+    EXPECT_THROW(status_unused.getSegmentType(), isc::InvalidOperation);
+}
+
 }
diff --git a/src/lib/datasrc/tests/factory_unittest.cc b/src/lib/datasrc/tests/factory_unittest.cc
index cc03ce3..58d6029 100644
--- a/src/lib/datasrc/tests/factory_unittest.cc
+++ b/src/lib/datasrc/tests/factory_unittest.cc
@@ -28,8 +28,6 @@ using namespace isc::datasrc;
 using namespace isc::data;
 
 std::string SQLITE_DBFILE_EXAMPLE_ORG = TEST_DATA_DIR "/example.org.sqlite3";
-const std::string STATIC_DS_FILE = TEST_DATA_DIR "/static.zone";
-const std::string STATIC_BAD_DS_FILE = TEST_DATA_DIR "/static-bad.zone";
 const std::string ROOT_ZONE_FILE = TEST_DATA_DIR "/root.zone";
 
 namespace {
@@ -166,55 +164,5 @@ TEST(FactoryTest, badType) {
                                            DataSourceError);
 }
 
-// Check the static data source can be loaded.
-TEST(FactoryTest, staticDS) {
-    // The only configuration is the file to load.
-    const ConstElementPtr config(new StringElement(STATIC_DS_FILE));
-    // Get the data source
-    DataSourceClientContainer dsc("static", config);
-    // And try getting something out to see if it really works.
-    DataSourceClient::FindResult
-        result(dsc.getInstance().findZone(isc::dns::Name("BIND")));
-    ASSERT_EQ(result::SUCCESS, result.code);
-    EXPECT_EQ(isc::dns::Name("BIND"), result.zone_finder->getOrigin());
-    EXPECT_EQ(isc::dns::RRClass::CH(), result.zone_finder->getClass());
-    const isc::dns::ConstRRsetPtr
-        version(result.zone_finder->find(isc::dns::Name("VERSION.BIND"),
-                                         isc::dns::RRType::TXT())->rrset);
-    ASSERT_NE(isc::dns::ConstRRsetPtr(), version);
-    EXPECT_EQ(isc::dns::Name("VERSION.BIND"), version->getName());
-    EXPECT_EQ(isc::dns::RRClass::CH(), version->getClass());
-    EXPECT_EQ(isc::dns::RRType::TXT(), version->getType());
-}
-
-// Check that file not containing BIND./CH is rejected
-TEST(FactoryTest, staticDSBadFile) {
-    // The only configuration is the file to load.
-    const ConstElementPtr config(new StringElement(STATIC_BAD_DS_FILE));
-    // See it does not want the file
-    EXPECT_THROW(DataSourceClientContainer("static", config), DataSourceError);
-}
-
-// Check that some bad configs are rejected
-TEST(FactoryTest, staticDSBadConfig) {
-    const char* configs[] = {
-        // The file does not exist
-        "\"/does/not/exist\"",
-        // Bad types
-        "null",
-        "42",
-        "{}",
-        "[]",
-        "true",
-        NULL
-    };
-    for (const char** config(configs); *config; ++config) {
-        SCOPED_TRACE(*config);
-        EXPECT_THROW(DataSourceClientContainer("static",
-                                               Element::fromJSON(*config)),
-                     DataSourceError);
-    }
-}
-
 } // end anonymous namespace
 
diff --git a/src/lib/datasrc/tests/memory/zone_table_segment_unittest.cc b/src/lib/datasrc/tests/memory/zone_table_segment_unittest.cc
index ac114e2..51d5e0f 100644
--- a/src/lib/datasrc/tests/memory/zone_table_segment_unittest.cc
+++ b/src/lib/datasrc/tests/memory/zone_table_segment_unittest.cc
@@ -31,8 +31,7 @@ namespace {
 class ZoneTableSegmentTest : public ::testing::Test {
 protected:
     ZoneTableSegmentTest() :
-        ztable_segment_(ZoneTableSegment::create(isc::data::NullElement(),
-                                                 RRClass::IN()))
+        ztable_segment_(ZoneTableSegment::create(RRClass::IN(), "local"))
     {}
 
     void TearDown() {
@@ -47,6 +46,10 @@ protected:
 TEST_F(ZoneTableSegmentTest, create) {
     // By default, a local zone table segment is created.
     EXPECT_NE(static_cast<void*>(NULL), ztable_segment_);
+
+    // Unknown types of segment are rejected.
+    EXPECT_THROW(ZoneTableSegment::create(RRClass::IN(), "unknown"),
+                 UnknownSegmentType);
 }
 
 // Helper function to check const and non-const methods.
diff --git a/src/lib/datasrc/tests/memory/zone_writer_unittest.cc b/src/lib/datasrc/tests/memory/zone_writer_unittest.cc
index 13bcc3b..5d2cd0a 100644
--- a/src/lib/datasrc/tests/memory/zone_writer_unittest.cc
+++ b/src/lib/datasrc/tests/memory/zone_writer_unittest.cc
@@ -16,7 +16,6 @@
 #include <datasrc/memory/zone_table_segment_local.h>
 #include <datasrc/memory/zone_data.h>
 
-#include <cc/data.h>
 #include <dns/rrclass.h>
 #include <dns/name.h>
 
@@ -38,11 +37,7 @@ class TestException {};
 class ZoneWriterLocalTest : public ::testing::Test {
 public:
     ZoneWriterLocalTest() :
-        // FIXME: The NullElement probably isn't the best one, but we don't
-        // know how the config will look, so it just fills the argument
-        // (which is currently ignored)
-        segment_(ZoneTableSegment::create(isc::data::NullElement(),
-                                          RRClass::IN())),
+        segment_(ZoneTableSegment::create(RRClass::IN(), "local")),
         writer_(new
             ZoneWriterLocal(dynamic_cast<ZoneTableSegmentLocal*>(segment_.
                                                                  get()),
diff --git a/src/lib/datasrc/tests/mock_client.cc b/src/lib/datasrc/tests/mock_client.cc
new file mode 100644
index 0000000..90e0a66
--- /dev/null
+++ b/src/lib/datasrc/tests/mock_client.cc
@@ -0,0 +1,197 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <datasrc/tests/mock_client.h>
+#include <datasrc/client.h>
+#include <datasrc/result.h>
+#include <datasrc/zone_iterator.h>
+#include <datasrc/data_source.h>
+
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/rrset.h>
+#include <dns/rrttl.h>
+#include <dns/rdataclass.h>
+
+#include <cc/data.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <vector>
+#include <set>
+#include <string>
+
+using namespace isc::dns;
+
+using boost::shared_ptr;
+using std::vector;
+using std::string;
+using std::set;
+
+namespace isc {
+namespace datasrc {
+namespace unittest {
+
+namespace {
+class Finder : public ZoneFinder {
+public:
+    Finder(const Name& origin) :
+        origin_(origin)
+    {}
+    Name getOrigin() const { return (origin_); }
+    // The rest is not to be called, so just have them
+    RRClass getClass() const {
+        isc_throw(isc::NotImplemented, "Not implemented");
+    }
+    shared_ptr<Context> find(const Name&, const RRType&,
+                             const FindOptions)
+    {
+        isc_throw(isc::NotImplemented, "Not implemented");
+    }
+    shared_ptr<Context> findAll(const Name&,
+                                vector<ConstRRsetPtr>&,
+                                const FindOptions)
+    {
+        isc_throw(isc::NotImplemented, "Not implemented");
+    }
+    FindNSEC3Result findNSEC3(const Name&, bool) {
+        isc_throw(isc::NotImplemented, "Not implemented");
+    }
+private:
+    Name origin_;
+};
+
+class Iterator : public ZoneIterator {
+public:
+    Iterator(const Name& origin, bool include_a) :
+        origin_(origin),
+        soa_(new RRset(origin_, RRClass::IN(), RRType::SOA(),
+                       RRTTL(3600)))
+    {
+        // The RData here is bogus, but it is not used to anything. There
+        // just needs to be some.
+        soa_->addRdata(rdata::generic::SOA(Name::ROOT_NAME(),
+                                           Name::ROOT_NAME(),
+                                           0, 0, 0, 0, 0));
+        rrsets_.push_back(soa_);
+
+        RRsetPtr rrset(new RRset(origin_, RRClass::IN(), RRType::NS(),
+                                 RRTTL(3600)));
+        rrset->addRdata(rdata::generic::NS(Name::ROOT_NAME()));
+        rrsets_.push_back(rrset);
+
+        if (include_a) {
+            // Dummy A rrset. This is used for checking zone data
+            // after reload.
+            rrset.reset(new RRset(Name("tstzonedata").concatenate(origin_),
+                                  RRClass::IN(), RRType::A(),
+                                  RRTTL(3600)));
+            rrset->addRdata(rdata::in::A("192.0.2.1"));
+            rrsets_.push_back(rrset);
+        }
+
+        rrsets_.push_back(ConstRRsetPtr());
+
+        it_ = rrsets_.begin();
+    }
+    virtual isc::dns::ConstRRsetPtr getNextRRset() {
+        ConstRRsetPtr result = *it_;
+        ++it_;
+        return (result);
+    }
+    virtual isc::dns::ConstRRsetPtr getSOA() const {
+        return (soa_);
+    }
+private:
+    const Name origin_;
+    const RRsetPtr soa_;
+    vector<ConstRRsetPtr> rrsets_;
+    vector<ConstRRsetPtr>::const_iterator it_;
+};
+}
+
+// A test data source. It pretends it has some zones.
+
+MockDataSourceClient::MockDataSourceClient(const char* zone_names[]) :
+    have_a_(true), use_baditerator_(true)
+{
+    for (const char** zone(zone_names); *zone; ++zone) {
+        zones.insert(Name(*zone));
+    }
+}
+
+// Constructor from configuration. The list of zones will be empty, but
+// it will keep the configuration inside for further inspection.
+MockDataSourceClient::MockDataSourceClient(
+    const string& type,
+    const data::ConstElementPtr& configuration) :
+    type_(type),
+    configuration_(configuration),
+    have_a_(true), use_baditerator_(true)
+{
+    EXPECT_NE("MasterFiles", type) << "MasterFiles is a special case "
+        "and it never should be created as a data source client";
+    if (configuration_->getType() == data::Element::list) {
+        for (size_t i(0); i < configuration_->size(); ++i) {
+            zones.insert(Name(configuration_->get(i)->stringValue()));
+        }
+    }
+}
+
+DataSourceClient::FindResult
+MockDataSourceClient::findZone(const Name& name) const {
+    if (zones.empty()) {
+        return (FindResult(result::NOTFOUND, ZoneFinderPtr()));
+    }
+    set<Name>::const_iterator it(zones.upper_bound(name));
+    if (it == zones.begin()) {
+        return (FindResult(result::NOTFOUND, ZoneFinderPtr()));
+    }
+    --it;
+    NameComparisonResult compar(it->compare(name));
+    const ZoneFinderPtr finder(new Finder(*it));
+    switch (compar.getRelation()) {
+    case NameComparisonResult::EQUAL:
+        return (FindResult(result::SUCCESS, finder));
+    case NameComparisonResult::SUPERDOMAIN:
+        return (FindResult(result::PARTIALMATCH, finder));
+    default:
+        return (FindResult(result::NOTFOUND, ZoneFinderPtr()));
+    }
+}
+
+// These methods are not used. They just need to be there to have
+// complete vtable.
+
+ZoneIteratorPtr
+MockDataSourceClient::getIterator(const Name& name, bool) const {
+    if (use_baditerator_ && name == Name("noiter.org")) {
+        isc_throw(isc::NotImplemented, "Asked not to be implemented");
+    } else if (use_baditerator_ && name == Name("null.org")) {
+        return (ZoneIteratorPtr());
+    } else {
+        FindResult result(findZone(name));
+        if (result.code == isc::datasrc::result::SUCCESS) {
+            return (ZoneIteratorPtr(new Iterator(name, have_a_)));
+        } else {
+            isc_throw(DataSourceError, "No such zone");
+        }
+    }
+}
+
+} // end of unittest
+} // end of datasrc
+} // end of isc
diff --git a/src/lib/datasrc/tests/mock_client.h b/src/lib/datasrc/tests/mock_client.h
new file mode 100644
index 0000000..ae7a0bf
--- /dev/null
+++ b/src/lib/datasrc/tests/mock_client.h
@@ -0,0 +1,71 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <datasrc/client.h>
+
+#include <dns/dns_fwd.h>
+#include <dns/rrset.h>
+
+#include <cc/data.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <set>
+#include <vector>
+
+namespace isc {
+namespace datasrc {
+namespace unittest {
+
+// A test data source. It pretends it has some zones.
+class MockDataSourceClient : public DataSourceClient {
+public:
+    // Constructor from a list of zones.
+    MockDataSourceClient(const char* zone_names[]);
+
+    // Constructor from configuration. The list of zones will be empty, but
+    // it will keep the configuration inside for further inspection.
+    MockDataSourceClient(const std::string& type,
+                         const data::ConstElementPtr& configuration);
+
+    virtual FindResult findZone(const dns::Name& name) const;
+    // These methods are not used. They just need to be there to have
+    // complete vtable.
+    virtual ZoneUpdaterPtr getUpdater(const dns::Name&, bool, bool) const {
+        isc_throw(isc::NotImplemented, "Not implemented");
+    }
+    virtual std::pair<ZoneJournalReader::Result, ZoneJournalReaderPtr>
+    getJournalReader(const dns::Name&, uint32_t, uint32_t) const
+    {
+        isc_throw(isc::NotImplemented, "Not implemented");
+    }
+    virtual ZoneIteratorPtr getIterator(const dns::Name& name, bool) const;
+    void disableA() { have_a_ = false; }
+    void disableBadIterator() { use_baditerator_ = false; }
+    const std::string type_;
+    const data::ConstElementPtr configuration_;
+
+private:
+    std::set<dns::Name> zones;
+    bool have_a_; // control the iterator behavior whether to include A record
+    bool use_baditerator_; // whether to use bogus zone iterators for tests
+};
+
+} // end of unittest
+} // end of datasrc
+} // end of isc
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/datasrc/tests/zone_finder_context_unittest.cc b/src/lib/datasrc/tests/zone_finder_context_unittest.cc
index a5c8a8f..614b1be 100644
--- a/src/lib/datasrc/tests/zone_finder_context_unittest.cc
+++ b/src/lib/datasrc/tests/zone_finder_context_unittest.cc
@@ -63,11 +63,9 @@ typedef DataSourceClientPtr (*ClientCreator)(RRClass, const Name&);
 
 // Creator for the in-memory client to be tested
 DataSourceClientPtr
-createInMemoryClient(RRClass zclass, const Name& zname)
-{
-    const ElementPtr config(Element::fromJSON("{}"));
+createInMemoryClient(RRClass zclass, const Name& zname) {
     shared_ptr<ZoneTableSegment> ztable_segment(
-        ZoneTableSegment::create(*config, zclass));
+        ZoneTableSegment::create(zclass, "local"));
     shared_ptr<InMemoryClient> client(new InMemoryClient(ztable_segment,
                                                          zclass));
     client->load(zname, TEST_ZONE_FILE);
diff --git a/src/lib/datasrc/tests/zone_loader_unittest.cc b/src/lib/datasrc/tests/zone_loader_unittest.cc
index 4b42185..eabcd65 100644
--- a/src/lib/datasrc/tests/zone_loader_unittest.cc
+++ b/src/lib/datasrc/tests/zone_loader_unittest.cc
@@ -31,6 +31,7 @@
 #include <boost/shared_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
 #include <boost/foreach.hpp>
+
 #include <string>
 #include <vector>
 
@@ -287,13 +288,26 @@ MockClient::getUpdater(const Name& name, bool replace, bool journaling) const {
 class ZoneLoaderTest : public ::testing::Test {
 protected:
     ZoneLoaderTest() :
-        rrclass_(RRClass::IN()),
-        ztable_segment_(memory::ZoneTableSegment::
-                        create(isc::data::NullElement(), rrclass_)),
-        source_client_(ztable_segment_, rrclass_)
-    {}
+        rrclass_(RRClass::IN())
+    {
+        // Use ROOT_NAME as a placeholder; it will be ignored if filename is
+        // null.
+        prepareSource(Name::ROOT_NAME(), NULL);
+    }
     void prepareSource(const Name& zone, const char* filename) {
-        source_client_.load(zone, string(TEST_DATA_DIR) + "/" + filename);
+        // Cleanup the existing data in the right order
+        source_client_.reset();
+        ztable_segment_.reset();
+
+        // (re)configure zone table, then (re)construct the in-memory client
+        // with it.
+        ztable_segment_.reset(memory::ZoneTableSegment::create(rrclass_,
+                                                               "local"));
+        source_client_.reset(new memory::InMemoryClient(ztable_segment_,
+                                                        rrclass_));
+        if (filename) {
+            source_client_->load(zone, string(TEST_DATA_DIR) + "/" + filename);
+        }
     }
 private:
     const RRClass rrclass_;
@@ -305,7 +319,7 @@ private:
     // But the shared pointer won't let us, will it?
     shared_ptr<memory::ZoneTableSegment> ztable_segment_;
 protected:
-    memory::InMemoryClient source_client_;
+    boost::scoped_ptr<memory::InMemoryClient> source_client_;
     // This one is mocked. It will help us see what is happening inside.
     // Also, mocking it is simpler than setting up an sqlite3 client.
     MockClient destination_client_;
@@ -314,7 +328,7 @@ protected:
 // Use the loader to load an unsigned zone.
 TEST_F(ZoneLoaderTest, copyUnsigned) {
     prepareSource(Name::ROOT_NAME(), "root.zone");
-    ZoneLoader loader(destination_client_, Name::ROOT_NAME(), source_client_);
+    ZoneLoader loader(destination_client_, Name::ROOT_NAME(), *source_client_);
     // It gets the updater directly in the constructor
     ASSERT_EQ(1, destination_client_.provided_updaters_.size());
     EXPECT_EQ(Name::ROOT_NAME(), destination_client_.provided_updaters_[0]);
@@ -355,7 +369,7 @@ TEST_F(ZoneLoaderTest, copyUnsigned) {
 // Try loading incrementally.
 TEST_F(ZoneLoaderTest, copyUnsignedIncremental) {
     prepareSource(Name::ROOT_NAME(), "root.zone");
-    ZoneLoader loader(destination_client_, Name::ROOT_NAME(), source_client_);
+    ZoneLoader loader(destination_client_, Name::ROOT_NAME(), *source_client_);
 
     // Try loading few RRs first.
     loader.loadIncremental(10);
@@ -390,7 +404,7 @@ TEST_F(ZoneLoaderTest, copyUnsignedIncremental) {
 TEST_F(ZoneLoaderTest, copySigned) {
     prepareSource(Name("example.org"), "example.org.nsec3-signed");
     ZoneLoader loader(destination_client_, Name("example.org"),
-                      source_client_);
+                      *source_client_);
     loader.load();
 
     // All the RRs are there, including the ones in NSEC3 namespace
@@ -416,13 +430,13 @@ TEST_F(ZoneLoaderTest, copyMissingDestination) {
     destination_client_.missing_zone_ = true;
     prepareSource(Name::ROOT_NAME(), "root.zone");
     EXPECT_THROW(ZoneLoader(destination_client_, Name::ROOT_NAME(),
-                            source_client_), DataSourceError);
+                            *source_client_), DataSourceError);
 }
 
 // If the source zone does not exist, it throws
 TEST_F(ZoneLoaderTest, copyMissingSource) {
     EXPECT_THROW(ZoneLoader(destination_client_, Name::ROOT_NAME(),
-                            source_client_), DataSourceError);
+                            *source_client_), DataSourceError);
 }
 
 // The class of the source and destination are different
@@ -430,7 +444,7 @@ TEST_F(ZoneLoaderTest, classMismatch) {
     destination_client_.rrclass_ = RRClass::CH();
     prepareSource(Name::ROOT_NAME(), "root.zone");
     EXPECT_THROW(ZoneLoader(destination_client_, Name::ROOT_NAME(),
-                            source_client_), isc::InvalidParameter);
+                            *source_client_), isc::InvalidParameter);
 }
 
 // Load an unsigned zone, all at once
@@ -593,7 +607,7 @@ TEST_F(ZoneLoaderTest, loadCheckWarn) {
 TEST_F(ZoneLoaderTest, copyCheckWarn) {
     prepareSource(Name("example.org"), "checkwarn.zone");
     ZoneLoader loader(destination_client_, Name("example.org"),
-                      source_client_);
+                      *source_client_);
     EXPECT_TRUE(loader.loadIncremental(10));
     // The messages go to the log. We don't have an easy way to examine them.
     // But the zone was committed and contains all 3 RRs
diff --git a/src/lib/dhcpsrv/pool.cc b/src/lib/dhcpsrv/pool.cc
index 7b8a4ea..7104c61 100644
--- a/src/lib/dhcpsrv/pool.cc
+++ b/src/lib/dhcpsrv/pool.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -64,7 +64,7 @@ Pool4::Pool4(const isc::asiolink::IOAddress& prefix,
 
 Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
              const isc::asiolink::IOAddress& last)
-    :Pool(first, last), type_(type), prefix_len_(0) {
+    :Pool(first, last), type_(type) {
 
     // check if specified address boundaries are sane
     if (!first.isV6() || !last.isV6()) {
@@ -95,16 +95,15 @@ Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
 Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& prefix,
              uint8_t prefix_len)
     :Pool(prefix, IOAddress("::")),
-     type_(type), prefix_len_(prefix_len) {
+     type_(type) {
 
     // check if the prefix is sane
     if (!prefix.isV6()) {
         isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
     }
 
-    // check if the prefix length is sane (we use the member variable only
-    // for silencing some compilers; see #2705 and #2789).
-    if (prefix_len_ == 0 || prefix_len_ > 128) {
+    // check if the prefix length is sane 
+    if (prefix_len == 0 || prefix_len > 128) {
         isc_throw(BadValue, "Invalid prefix length");
     }
 
diff --git a/src/lib/dhcpsrv/pool.h b/src/lib/dhcpsrv/pool.h
index e8dc3e3..e0a6f3c 100644
--- a/src/lib/dhcpsrv/pool.h
+++ b/src/lib/dhcpsrv/pool.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -168,9 +168,6 @@ private:
     /// @brief defines a pool type
     Pool6Type type_;
 
-    /// @brief prefix length
-    /// used by TYPE_PD only (zeroed for other types)
-    uint8_t prefix_len_;
 };
 
 /// @brief a pointer an IPv6 Pool
diff --git a/src/lib/dns/gen-rdatacode.py.in b/src/lib/dns/gen-rdatacode.py.in
index d0a2576..0d946b9 100755
--- a/src/lib/dns/gen-rdatacode.py.in
+++ b/src/lib/dns/gen-rdatacode.py.in
@@ -33,6 +33,7 @@ import sys
 # Example:
 #     new_rdata_factory_users = [('a', 'in'), ('a', 'ch'), ('soa', 'generic')]
 new_rdata_factory_users = [('a', 'in'), ('aaaa', 'in'),
+                           ('afsdb', 'generic'),
                            ('cname', 'generic'),
                            ('dlv', 'generic'),
                            ('dname', 'generic'),
diff --git a/src/lib/dns/rdata/generic/afsdb_18.cc b/src/lib/dns/rdata/generic/afsdb_18.cc
index ec76ee0..cf6a35c 100644
--- a/src/lib/dns/rdata/generic/afsdb_18.cc
+++ b/src/lib/dns/rdata/generic/afsdb_18.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -25,9 +25,12 @@
 
 #include <boost/lexical_cast.hpp>
 
+#include <dns/rdata/generic/detail/lexer_util.h>
+
 using namespace std;
+using boost::lexical_cast;
 using namespace isc::util;
-using namespace isc::util::str;
+using isc::dns::rdata::generic::detail::createNameFromLexer;
 
 // BEGIN_ISC_NAMESPACE
 // BEGIN_RDATA_NAMESPACE
@@ -52,24 +55,58 @@ using namespace isc::util::str;
 AFSDB::AFSDB(const std::string& afsdb_str) :
     subtype_(0), server_(Name::ROOT_NAME())
 {
-    istringstream iss(afsdb_str);
-
     try {
-        const uint32_t subtype = tokenToNum<int32_t, 16>(getToken(iss));
-        const Name servername(getToken(iss));
+        std::istringstream ss(afsdb_str);
+        MasterLexer lexer;
+        lexer.pushSource(ss);
 
-        if (!iss.eof()) {
-            isc_throw(InvalidRdataText, "Unexpected input for AFSDB"
-                    "RDATA: " << afsdb_str);
+        createFromLexer(lexer, NULL);
+
+        if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) {
+            isc_throw(InvalidRdataText, "extra input text for AFSDB: "
+                      << afsdb_str);
         }
+    } catch (const MasterLexer::LexerError& ex) {
+        isc_throw(InvalidRdataText, "Failed to construct AFSDB from '" <<
+                  afsdb_str << "': " << ex.what());
+    }
+}
 
-        subtype_ = subtype;
-        server_ = servername;
+/// \brief Constructor with a context of MasterLexer.
+///
+/// The \c lexer should point to the beginning of valid textual representation
+/// of an AFSDB RDATA.  The SERVER field can be non-absolute if \c origin
+/// is non-NULL, in which case \c origin is used to make it absolute.
+/// It must not be represented as a quoted string.
+///
+/// The SUBTYPE field must be a valid decimal representation of an
+/// unsigned 16-bit integer.
+///
+/// \throw MasterLexer::LexerError General parsing error such as missing field.
+/// \throw Other Exceptions from the Name and RRTTL constructors if
+/// construction of textual fields as these objects fail.
+///
+/// \param lexer A \c MasterLexer object parsing a master file for the
+/// RDATA to be created
+/// \param origin If non NULL, specifies the origin of SERVER when it
+/// is non-absolute.
+AFSDB::AFSDB(MasterLexer& lexer, const Name* origin,
+       MasterLoader::Options, MasterLoaderCallbacks&) :
+    subtype_(0), server_(".")
+{
+    createFromLexer(lexer, origin);
+}
 
-    } catch (const StringTokenError& ste) {
-        isc_throw(InvalidRdataText, "Invalid AFSDB text: " <<
-                  ste.what() << ": " << afsdb_str);
+void
+AFSDB::createFromLexer(MasterLexer& lexer, const Name* origin)
+{
+    const uint32_t num = lexer.getNextToken(MasterToken::NUMBER).getNumber();
+    if (num > 65535) {
+        isc_throw(InvalidRdataText, "Invalid AFSDB subtype: " << num);
     }
+    subtype_ = static_cast<uint16_t>(num);
+
+    server_ = createNameFromLexer(lexer, origin);
 }
 
 /// \brief Constructor from wire-format data.
@@ -111,7 +148,7 @@ AFSDB::operator=(const AFSDB& source) {
 /// \return A \c string object that represents the \c AFSDB object.
 string
 AFSDB::toText() const {
-    return (boost::lexical_cast<string>(subtype_) + " " + server_.toText());
+    return (lexical_cast<string>(subtype_) + " " + server_.toText());
 }
 
 /// \brief Render the \c AFSDB in the wire format without name compression.
diff --git a/src/lib/dns/rdata/generic/afsdb_18.h b/src/lib/dns/rdata/generic/afsdb_18.h
index 4a46775..359a6e4 100644
--- a/src/lib/dns/rdata/generic/afsdb_18.h
+++ b/src/lib/dns/rdata/generic/afsdb_18.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -61,6 +61,8 @@ public:
     uint16_t getSubtype() const;
 
 private:
+    void createFromLexer(MasterLexer& lexer, const Name* origin);
+
     uint16_t subtype_;
     Name server_;
 };
diff --git a/src/lib/dns/tests/rdata_afsdb_unittest.cc b/src/lib/dns/tests/rdata_afsdb_unittest.cc
index 9bb64b7..9a628cd 100644
--- a/src/lib/dns/tests/rdata_afsdb_unittest.cc
+++ b/src/lib/dns/tests/rdata_afsdb_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -34,7 +34,7 @@ using namespace isc::dns::rdata;
 const char* const afsdb_text = "1 afsdb.example.com.";
 const char* const afsdb_text2 = "0 root.example.com.";
 const char* const too_long_label("012345678901234567890123456789"
-        "0123456789012345678901234567890123");
+        "0123456789012345678901234567890123.");
 
 namespace {
 class Rdata_AFSDB_Test : public RdataTest {
@@ -68,9 +68,17 @@ TEST_F(Rdata_AFSDB_Test, badText) {
     // number of fields (must be 2) is incorrect
     EXPECT_THROW(const generic::AFSDB rdata_afsdb("10 afsdb. example.com."),
                  InvalidRdataText);
+    // No origin and relative
+    EXPECT_THROW(const generic::AFSDB rdata_afsdb("1 afsdb.example.com"),
+                 MissingNameOrigin);
     // bad name
     EXPECT_THROW(const generic::AFSDB rdata_afsdb("1 afsdb.example.com." +
-                string(too_long_label)), TooLongLabel);
+                 string(too_long_label)), TooLongLabel);
+}
+
+TEST_F(Rdata_AFSDB_Test, copy) {
+    const generic::AFSDB rdata_afsdb2(rdata_afsdb);
+    EXPECT_EQ(0, rdata_afsdb.compare(rdata_afsdb2));
 }
 
 TEST_F(Rdata_AFSDB_Test, assignment) {
@@ -119,9 +127,24 @@ TEST_F(Rdata_AFSDB_Test, createFromLexer) {
         *test::createRdataUsingLexer(RRType::AFSDB(), RRClass::IN(),
                                      afsdb_text)));
 
+    // test::createRdataUsingLexer() constructs relative to
+    // "example.org." origin.
+    generic::AFSDB tmp = generic::AFSDB("1 afsdb2.example.org.");
+    EXPECT_EQ(0, tmp.compare(
+        *test::createRdataUsingLexer(RRType::AFSDB(), RRClass::IN(),
+                                     "1 afsdb2")));
+
     // Exceptions cause NULL to be returned.
     EXPECT_FALSE(test::createRdataUsingLexer(RRType::AFSDB(), RRClass::IN(),
                                              "1root.example.com."));
+
+    // 65536 is larger than maximum possible subtype
+    EXPECT_FALSE(test::createRdataUsingLexer(RRType::AFSDB(), RRClass::IN(),
+                                             "65536 afsdb.example.com."));
+
+    // Extra text at end of line
+    EXPECT_FALSE(test::createRdataUsingLexer(RRType::AFSDB(), RRClass::IN(),
+                                             "1 afsdb.example.com. extra."));
 }
 
 TEST_F(Rdata_AFSDB_Test, toWireBuffer) {
@@ -197,9 +220,9 @@ TEST_F(Rdata_AFSDB_Test, compare) {
     EXPECT_EQ(0, rdata_afsdb.compare(generic::AFSDB("1 "
                                 "AFSDB.example.com.")));
 
-    const generic::AFSDB small1("10 afsdb.example.com");
-    const generic::AFSDB large1("65535 afsdb.example.com");
-    const generic::AFSDB large2("256 afsdb.example.com");
+    const generic::AFSDB small1("10 afsdb.example.com.");
+    const generic::AFSDB large1("65535 afsdb.example.com.");
+    const generic::AFSDB large2("256 afsdb.example.com.");
 
     // confirm these are compared as unsigned values
     EXPECT_GT(0, rdata_afsdb.compare(large1));
@@ -210,7 +233,7 @@ TEST_F(Rdata_AFSDB_Test, compare) {
     EXPECT_LT(0, large2.compare(small1));
 
     // another AFSDB whose server name is larger than that of rdata_afsdb.
-    const generic::AFSDB large3("256 zzzzz.example.com");
+    const generic::AFSDB large3("256 zzzzz.example.com.");
     EXPECT_GT(0, large2.compare(large3));
     EXPECT_LT(0, large3.compare(large2));
 
diff --git a/src/lib/python/isc/cc/Makefile.am b/src/lib/python/isc/cc/Makefile.am
index f7c5b00..fe7d747 100644
--- a/src/lib/python/isc/cc/Makefile.am
+++ b/src/lib/python/isc/cc/Makefile.am
@@ -7,6 +7,7 @@ pylogmessagedir = $(pyexecdir)/isc/log_messages/
 
 CLEANFILES = $(PYTHON_LOGMSGPKG_DIR)/work/pycc_messages.py
 CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/pycc_messages.pyc
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/pycc_messages.pyo
 
 EXTRA_DIST = pycc_messages.mes proto_defs.py
 
diff --git a/src/lib/python/isc/cc/cc_generated/Makefile.am b/src/lib/python/isc/cc/cc_generated/Makefile.am
index 87e49c1..bc8d478 100644
--- a/src/lib/python/isc/cc/cc_generated/Makefile.am
+++ b/src/lib/python/isc/cc/cc_generated/Makefile.am
@@ -26,6 +26,7 @@ CLEANDIRS = __pycache__
 
 CLEANFILES = proto_defs.py __init__.py
 CLEANFILES += proto_defs.pyc __init__.pyc
+CLEANFILES += proto_defs.pyo __init__.pyo
 
 clean-local:
 	rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/cc/session.py b/src/lib/python/isc/cc/session.py
index 036c078..fced424 100644
--- a/src/lib/python/isc/cc/session.py
+++ b/src/lib/python/isc/cc/session.py
@@ -57,11 +57,11 @@ class Session:
         try:
             self._socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
             self._socket.connect(self.socket_file)
-            self.sendmsg({ "type": "getlname" })
+            self.sendmsg({ CC_HEADER_TYPE: CC_COMMAND_GET_LNAME })
             env, msg = self.recvmsg(False)
             if not env:
                 raise ProtocolError("Could not get local name")
-            self._lname = msg["lname"]
+            self._lname = msg[CC_PAYLOAD_LNAME]
             if not self._lname:
                 raise ProtocolError("Could not get local name")
             logger.debug(logger.DBGLVL_TRACE_BASIC, PYCC_LNAME_RECEIVED,
@@ -125,9 +125,10 @@ class Session:
             if len(self._queue) > 0:
                 i = 0;
                 for env, msg in self._queue:
-                    if seq != None and "reply" in env and seq == env["reply"]:
+                    if seq != None and CC_HEADER_REPLY in env and \
+                        seq == env[CC_HEADER_REPLY]:
                         return self._queue.pop(i)
-                    elif seq == None and "reply" not in env:
+                    elif seq == None and CC_HEADER_REPLY not in env:
                         return self._queue.pop(i)
                     else:
                         i = i + 1
@@ -141,7 +142,9 @@ class Session:
                     if data_length > 0:
                         env = isc.cc.message.from_wire(data[2:header_length+2])
                         msg = isc.cc.message.from_wire(data[header_length + 2:])
-                        if (seq == None and "reply" not in env) or (seq != None and "reply" in env and seq == env["reply"]):
+                        if (seq == None and CC_HEADER_REPLY not in env) or \
+                            (seq != None and CC_HEADER_REPLY in env and
+                             seq == env[CC_HEADER_REPLY]):
                             return env, msg
                         else:
                             self._queue.append((env,msg))
@@ -248,18 +251,18 @@ class Session:
         self._sequence += 1
         return self._sequence
 
-    def group_subscribe(self, group, instance = "*"):
+    def group_subscribe(self, group, instance=CC_INSTANCE_WILDCARD):
         self.sendmsg({
-            "type": "subscribe",
-            "group": group,
-            "instance": instance,
+            CC_HEADER_TYPE: CC_COMMAND_SUBSCRIBE,
+            CC_HEADER_GROUP: group,
+            CC_HEADER_INSTANCE: instance,
         })
 
-    def group_unsubscribe(self, group, instance = "*"):
+    def group_unsubscribe(self, group, instance=CC_INSTANCE_WILDCARD):
         self.sendmsg({
-            "type": "unsubscribe",
-            "group": group,
-            "instance": instance,
+            CC_HEADER_TYPE: CC_COMMAND_UNSUBSCRIBE,
+            CC_HEADER_GROUP: group,
+            CC_HEADER_INSTANCE: instance,
         })
 
     def group_sendmsg(self, msg, group, instance=CC_INSTANCE_WILDCARD,
@@ -308,13 +311,13 @@ class Session:
     def group_reply(self, routing, msg):
         seq = self._next_sequence()
         self.sendmsg({
-            "type": "send",
-            "from": self._lname,
-            "to": routing["from"],
-            "group": routing["group"],
-            "instance": routing["instance"],
-            "seq": seq,
-            "reply": routing["seq"],
+            CC_HEADER_TYPE: CC_COMMAND_SEND,
+            CC_HEADER_FROM: self._lname,
+            CC_HEADER_TO: routing[CC_HEADER_FROM],
+            CC_HEADER_GROUP: routing[CC_HEADER_GROUP],
+            CC_HEADER_INSTANCE: routing[CC_HEADER_INSTANCE],
+            CC_HEADER_SEQ: seq,
+            CC_HEADER_REPLY: routing[CC_HEADER_SEQ],
         }, isc.cc.message.to_wire(msg))
         return seq
 
diff --git a/src/lib/python/isc/config/ccsession.py b/src/lib/python/isc/config/ccsession.py
index b592d8d..c6d9791 100644
--- a/src/lib/python/isc/config/ccsession.py
+++ b/src/lib/python/isc/config/ccsession.py
@@ -82,22 +82,22 @@ def parse_answer(msg):
        containing an error message"""
     if type(msg) != dict:
         raise ModuleCCSessionError("Answer message is not a dict: " + str(msg))
-    if 'result' not in msg:
+    if CC_PAYLOAD_RESULT not in msg:
         raise ModuleCCSessionError("answer message does not contain 'result' element")
-    elif type(msg['result']) != list:
+    elif type(msg[CC_PAYLOAD_RESULT]) != list:
         raise ModuleCCSessionError("wrong result type in answer message")
-    elif len(msg['result']) < 1:
+    elif len(msg[CC_PAYLOAD_RESULT]) < 1:
         raise ModuleCCSessionError("empty result list in answer message")
-    elif type(msg['result'][0]) != int:
+    elif type(msg[CC_PAYLOAD_RESULT][0]) != int:
         raise ModuleCCSessionError("wrong rcode type in answer message")
     else:
-        if len(msg['result']) > 1:
-            if (msg['result'][0] != CC_REPLY_SUCCESS and
-                type(msg['result'][1]) != str):
+        if len(msg[CC_PAYLOAD_RESULT]) > 1:
+            if (msg[CC_PAYLOAD_RESULT][0] != CC_REPLY_SUCCESS and
+                type(msg[CC_PAYLOAD_RESULT][1]) != str):
                 raise ModuleCCSessionError("rcode in answer message is non-zero, value is not a string")
-            return msg['result'][0], msg['result'][1]
+            return msg[CC_PAYLOAD_RESULT][0], msg[CC_PAYLOAD_RESULT][1]
         else:
-            return msg['result'][0], None
+            return msg[CC_PAYLOAD_RESULT][0], None
 
 def create_answer(rcode, arg = None):
     """Creates an answer packet for config&commands. rcode must be an
@@ -109,9 +109,9 @@ def create_answer(rcode, arg = None):
     if rcode != CC_REPLY_SUCCESS and type(arg) != str:
         raise ModuleCCSessionError("arg in create_answer for rcode != 0 must be a string describing the error")
     if arg != None:
-        return { 'result': [ rcode, arg ] }
+        return { CC_PAYLOAD_RESULT: [ rcode, arg ] }
     else:
-        return { 'result': [ rcode ] }
+        return { CC_PAYLOAD_RESULT: [ rcode ] }
 
 # 'fixed' commands
 """Fixed names for command and configuration messages"""
@@ -133,7 +133,7 @@ def parse_command(msg):
        string. If it is not, this function returns None, None"""
     if type(msg) == dict and len(msg.items()) == 1:
         cmd, value = msg.popitem()
-        if cmd == "command" and type(value) == list:
+        if cmd == CC_PAYLOAD_COMMAND and type(value) == list:
             if len(value) == 1 and type(value[0]) == str:
                 return value[0], None
             elif len(value) > 1 and type(value[0]) == str:
@@ -150,7 +150,7 @@ def create_command(command_name, params = None):
     cmd = [ command_name ]
     if params:
         cmd.append(params)
-    msg = { 'command': cmd }
+    msg = { CC_PAYLOAD_COMMAND: cmd }
     return msg
 
 def default_logconfig_handler(new_config, config_data):
@@ -215,7 +215,7 @@ class ModuleCCSession(ConfigData):
             self._session = Session(socket_file)
         else:
             self._session = cc_session
-        self._session.group_subscribe(self._module_name, "*")
+        self._session.group_subscribe(self._module_name, CC_INSTANCE_WILDCARD)
 
         self._remote_module_configs = {}
         self._remote_module_callbacks = {}
@@ -228,7 +228,8 @@ class ModuleCCSession(ConfigData):
         # If the CC Session obejct has been closed, it returns
         # immediately.
         if self._session._closed: return
-        self._session.group_unsubscribe(self._module_name, "*")
+        self._session.group_unsubscribe(self._module_name,
+                                        CC_INSTANCE_WILDCARD)
         for module_name in self._remote_module_configs:
             self._session.group_unsubscribe(module_name)
 
@@ -294,10 +295,10 @@ class ModuleCCSession(ConfigData):
            functions if present. Responds on the channel if the
            handler returns a message."""
         # should we default to an answer? success-by-default? unhandled error?
-        if msg is not None and not 'result' in msg:
+        if msg is not None and not CC_PAYLOAD_RESULT in msg:
             answer = None
             try:
-                module_name = env['group']
+                module_name = env[CC_HEADER_GROUP]
                 cmd, arg = isc.config.ccsession.parse_command(msg)
                 if cmd == COMMAND_CONFIG_UPDATE:
                     new_config = arg
diff --git a/src/lib/python/isc/config/cfgmgr.py b/src/lib/python/isc/config/cfgmgr.py
index 82c8e6c..fe4924c 100644
--- a/src/lib/python/isc/config/cfgmgr.py
+++ b/src/lib/python/isc/config/cfgmgr.py
@@ -28,6 +28,7 @@ import tempfile
 import json
 import errno
 from isc.cc import data
+from isc.cc.proto_defs import *
 from isc.config import ccsession, config_data, module_spec
 from isc.util.file import path_search
 import bind10_config
@@ -603,7 +604,7 @@ class ConfigManager:
             # ignore 'None' value (even though they should not occur)
             # and messages that are answers to questions we did
             # not ask
-            if msg is not None and not 'result' in msg:
+            if msg is not None and not CC_PAYLOAD_RESULT in msg:
                 answer = self.handle_msg(msg);
                 # Only respond if there actually is something to respond with
                 if answer is not None:
diff --git a/src/lib/python/isc/datasrc/tests/datasrc_test.py b/src/lib/python/isc/datasrc/tests/datasrc_test.py
index 1ab2148..1649fc1 100644
--- a/src/lib/python/isc/datasrc/tests/datasrc_test.py
+++ b/src/lib/python/isc/datasrc/tests/datasrc_test.py
@@ -33,8 +33,11 @@ WRITE_ZONE_DB_FILE = TESTDATA_WRITE_PATH + "rwtest.sqlite3.copied"
 
 READ_ZONE_DB_CONFIG = "{ \"database_file\": \"" + READ_ZONE_DB_FILE + "\" }"
 WRITE_ZONE_DB_CONFIG = "{ \"database_file\": \"" + WRITE_ZONE_DB_FILE + "\"}"
-
-STATIC_ZONE_CONFIG = '"' + TESTDATA_PATH + "static.zone" + '"'
+# In-memory data source must be built via client list.
+INMEMORY_ZONE_CONFIG = '''[{
+   "type": "MasterFiles",
+   "cache-enable": true,
+   "params": {"example.com": "''' + TESTDATA_PATH + 'example.com"}}]'
 
 def add_rrset(rrset_list, name, rrclass, rrtype, ttl, rdatas):
     rrset_to_add = isc.dns.RRset(name, rrclass, rrtype, ttl)
@@ -563,11 +566,10 @@ class DataSrcUpdater(unittest.TestCase):
         self.assertEqual(updater_refs, sys.getrefcount(updater))
 
     def test_two_modules(self):
-        # load two modules, and check if they don't interfere; as the
-        # memory datasource module no longer exists, we check the static
-        # datasource instead (as that uses the memory datasource
-        # anyway).
-        dsc_static = isc.datasrc.DataSourceClient("static", STATIC_ZONE_CONFIG)
+        # load two modules, and check if they don't interfere.
+        clist = isc.datasrc.ConfigurableClientList(isc.dns.RRClass.IN)
+        clist.configure(INMEMORY_ZONE_CONFIG, True)
+        dsc_static = clist.find(isc.dns.Name("example.com"), True, False)[0]
         dsc_sql = isc.datasrc.DataSourceClient("sqlite3", READ_ZONE_DB_CONFIG)
 
         # check if exceptions are working
@@ -724,10 +726,9 @@ class DataSrcUpdater(unittest.TestCase):
         self.assertTrue(dsc.delete_zone(zone_name))
 
     def test_create_zone_not_implemented(self):
-        # As the memory datasource module no longer exists, we check the
-        # static datasource instead (as that uses the memory datasource
-        # anyway).
-        dsc = isc.datasrc.DataSourceClient("static", STATIC_ZONE_CONFIG)
+        clist = isc.datasrc.ConfigurableClientList(isc.dns.RRClass.IN)
+        clist.configure(INMEMORY_ZONE_CONFIG, True)
+        dsc = clist.find(isc.dns.Name("example.com"), True, False)[0]
         self.assertRaises(isc.datasrc.NotImplemented, dsc.create_zone,
                           isc.dns.Name("example.com"))
 
diff --git a/src/lib/python/isc/datasrc/tests/zone_loader_test.py b/src/lib/python/isc/datasrc/tests/zone_loader_test.py
index e437e36..4811aba 100644
--- a/src/lib/python/isc/datasrc/tests/zone_loader_test.py
+++ b/src/lib/python/isc/datasrc/tests/zone_loader_test.py
@@ -34,7 +34,11 @@ ORIG_DB_FILE = TESTDATA_PATH + '/example.com.sqlite3'
 DB_FILE = TESTDATA_WRITE_PATH + '/zoneloadertest.sqlite3'
 DB_CLIENT_CONFIG = '{ "database_file": "' + DB_FILE + '" }'
 DB_SOURCE_CLIENT_CONFIG = '{ "database_file": "' + SOURCE_DB_FILE + '" }'
-STATIC_ZONE_CONFIG = '"' + TESTDATA_PATH + "/static.zone" + '"'
+# In-memory data source must be built via client list.
+INMEMORY_ZONE_CONFIG = '''[{
+   "type": "MasterFiles",
+   "cache-enable": true,
+   "params": {"example.com": "''' + TESTDATA_PATH + '/example.com"}}]'
 
 ORIG_SOA_TXT = 'example.com. 3600 IN SOA master.example.com. ' +\
                'admin.example.com. 1234 3600 1800 2419200 7200\n'
@@ -216,15 +220,11 @@ class ZoneLoaderTests(unittest.TestCase):
                           self.client, zone_name, self.source_client)
 
     def test_no_ds_load_support(self):
-        # As the memory datasource module no longer exists, we check the
-        # static datasource instead (as that uses the memory datasource
-        # anyway).
-        #
-        # This may change in the future, but ATM, the static ds does not
-        # support the API the zone loader uses (it has direct load
-        # calls).
-        inmem_client = isc.datasrc.DataSourceClient('static',
-                                                    STATIC_ZONE_CONFIG);
+        # This may change in the future, but ATM, in-memory ds does not
+        # support the API the zone loader uses.
+        clist = isc.datasrc.ConfigurableClientList(isc.dns.RRClass.IN)
+        clist.configure(INMEMORY_ZONE_CONFIG, True)
+        inmem_client = clist.find(isc.dns.Name("example.com"), True, False)[0]
         self.assertRaises(isc.datasrc.NotImplemented,
                           isc.datasrc.ZoneLoader,
                           inmem_client, self.test_name, self.test_file)
@@ -239,8 +239,10 @@ class ZoneLoaderTests(unittest.TestCase):
         # For ds->ds loading, wrong class is detected upon construction
         # Need a bit of the extended setup for CH source client
         clientlist = isc.datasrc.ConfigurableClientList(isc.dns.RRClass.CH)
-        clientlist.configure('[ { "type": "static", "params": "' +
-                             STATIC_ZONE_FILE +'" } ]', False)
+        clientlist.configure('[ { "type": "MasterFiles", ' +
+                             '"cache-enable": true, ' +
+                             '"params": {"BIND": "' +
+                             STATIC_ZONE_FILE + '" }} ]', True)
         self.source_client, _, _ = clientlist.find(isc.dns.Name("bind."),
                                                    False, False)
         self.assertRaises(isc.dns.InvalidParameter, isc.datasrc.ZoneLoader,
diff --git a/src/lib/python/isc/notify/notify_out.py b/src/lib/python/isc/notify/notify_out.py
index 8b0a77c..7ae5665 100644
--- a/src/lib/python/isc/notify/notify_out.py
+++ b/src/lib/python/isc/notify/notify_out.py
@@ -26,6 +26,7 @@ from isc.net import addr
 import isc
 from isc.log_messages.notify_out_messages import *
 from isc.statistics import Counters
+from isc.util.address_formatter import AddressFormatter
 
 logger = isc.log.Logger("notify_out")
 
@@ -433,13 +434,13 @@ class NotifyOut:
                     self._notify_next_target(zone_notify_info)
 
         elif event_type == _EVENT_TIMEOUT and zone_notify_info.notify_try_num > 0:
-            logger.info(NOTIFY_OUT_TIMEOUT, tgt[0], tgt[1])
+            logger.info(NOTIFY_OUT_TIMEOUT, AddressFormatter(tgt))
 
         tgt = zone_notify_info.get_current_notify_target()
         if tgt:
             zone_notify_info.notify_try_num += 1
             if zone_notify_info.notify_try_num > _MAX_NOTIFY_TRY_NUM:
-                logger.warn(NOTIFY_OUT_RETRY_EXCEEDED, tgt[0], tgt[1],
+                logger.warn(NOTIFY_OUT_RETRY_EXCEEDED, AddressFormatter(tgt),
                             _MAX_NOTIFY_TRY_NUM)
                 self._notify_next_target(zone_notify_info)
             else:
@@ -487,15 +488,14 @@ class NotifyOut:
             elif zone_notify_info.get_socket().family == socket.AF_INET6:
                 self._counters.inc('zones', zone_notify_info.zone_name,
                                   'notifyoutv6')
-            logger.info(NOTIFY_OUT_SENDING_NOTIFY, addrinfo[0],
-                        addrinfo[1])
+            logger.info(NOTIFY_OUT_SENDING_NOTIFY, AddressFormatter(addrinfo))
         except (socket.error, addr.InvalidAddress) as err:
-            logger.error(NOTIFY_OUT_SOCKET_ERROR, addrinfo[0],
-                         addrinfo[1], err)
+            logger.error(NOTIFY_OUT_SOCKET_ERROR, AddressFormatter(addrinfo),
+                         err)
             return False
         except addr.InvalidAddress as iae:
-            logger.error(NOTIFY_OUT_INVALID_ADDRESS, addrinfo[0],
-                         addrinfo[1], iae)
+            logger.error(NOTIFY_OUT_INVALID_ADDRESS,
+                         AddressFormatter(addrinfo), iae)
             return False
 
         return True
@@ -544,26 +544,28 @@ class NotifyOut:
         try:
             msg.from_wire(msg_data)
             if not msg.get_header_flag(Message.HEADERFLAG_QR):
-                logger.warn(NOTIFY_OUT_REPLY_QR_NOT_SET, from_addr[0],
-                            from_addr[1])
+                logger.warn(NOTIFY_OUT_REPLY_QR_NOT_SET,
+                            AddressFormatter(from_addr))
                 return _BAD_QR
 
             if msg.get_qid() != zone_notify_info.notify_msg_id:
-                logger.warn(NOTIFY_OUT_REPLY_BAD_QID, from_addr[0],
-                            from_addr[1], msg.get_qid(),
+                logger.warn(NOTIFY_OUT_REPLY_BAD_QID,
+                            AddressFormatter(from_addr), msg.get_qid(),
                             zone_notify_info.notify_msg_id)
                 return _BAD_QUERY_ID
 
             question = msg.get_question()[0]
             if question.get_name() != Name(zone_notify_info.zone_name):
-                logger.warn(NOTIFY_OUT_REPLY_BAD_QUERY_NAME, from_addr[0],
-                            from_addr[1], question.get_name().to_text(),
+                logger.warn(NOTIFY_OUT_REPLY_BAD_QUERY_NAME,
+                            AddressFormatter(from_addr),
+                            question.get_name().to_text(),
                             Name(zone_notify_info.zone_name).to_text())
                 return _BAD_QUERY_NAME
 
             if msg.get_opcode() != Opcode.NOTIFY:
-                logger.warn(NOTIFY_OUT_REPLY_BAD_OPCODE, from_addr[0],
-                            from_addr[1], msg.get_opcode().to_text())
+                logger.warn(NOTIFY_OUT_REPLY_BAD_OPCODE,
+                            AddressFormatter(from_addr),
+                            msg.get_opcode().to_text())
                 return _BAD_OPCODE
         except Exception as err:
             # We don't care what exception, just report it?
@@ -580,8 +582,8 @@ class NotifyOut:
         try:
             msg, addr = sock.recvfrom(512)
         except socket.error as err:
-            logger.error(NOTIFY_OUT_SOCKET_RECV_ERROR, tgt_addr[0],
-                         tgt_addr[1], err)
+            logger.error(NOTIFY_OUT_SOCKET_RECV_ERROR,
+                         AddressFormatter(tgt_addr), err)
             return None
 
         return msg
diff --git a/src/lib/python/isc/notify/notify_out_messages.mes b/src/lib/python/isc/notify/notify_out_messages.mes
index 8d17640..30fb087 100644
--- a/src/lib/python/isc/notify/notify_out_messages.mes
+++ b/src/lib/python/isc/notify/notify_out_messages.mes
@@ -27,7 +27,7 @@ because notify_out first identifies a list of available zones before
 this process.  So this means some critical inconsistency in the data
 source or software bug.
 
-% NOTIFY_OUT_INVALID_ADDRESS invalid address %1#%2: %3
+% NOTIFY_OUT_INVALID_ADDRESS invalid address %1: %2
 The notify_out library tried to send a notify message to the given
 address, but it appears to be an invalid address. The configuration
 for secondary nameservers might contain a typographic error, or a
@@ -35,26 +35,26 @@ different BIND 10 module has forgotten to validate its data before
 sending this module a notify command. As such, this should normally
 not happen, and points to an oversight in a different module.
 
-% NOTIFY_OUT_REPLY_BAD_OPCODE bad opcode in notify reply from %1#%2: %3
+% NOTIFY_OUT_REPLY_BAD_OPCODE bad opcode in notify reply from %1: %2
 The notify_out library sent a notify message to the nameserver at
 the given address, but the response did not have the opcode set to
 NOTIFY. The opcode in the response is printed. Since there was a
 response, no more notifies will be sent to this server for this
 notification event.
 
-% NOTIFY_OUT_REPLY_BAD_QID bad QID in notify reply from %1#%2: got %3, should be %4
+% NOTIFY_OUT_REPLY_BAD_QID bad QID in notify reply from %1: got %2, should be %3
 The notify_out library sent a notify message to the nameserver at
 the given address, but the query id in the response does not match
 the one we sent. Since there was a response, no more notifies will
 be sent to this server for this notification event.
 
-% NOTIFY_OUT_REPLY_BAD_QUERY_NAME bad query name in notify reply from %1#%2: got %3, should be %4
+% NOTIFY_OUT_REPLY_BAD_QUERY_NAME bad query name in notify reply from %1: got %2, should be %3
 The notify_out library sent a notify message to the nameserver at
 the given address, but the query name in the response does not match
 the one we sent. Since there was a response, no more notifies will
 be sent to this server for this notification event.
 
-% NOTIFY_OUT_REPLY_QR_NOT_SET QR flags set to 0 in reply to notify from %1#%2
+% NOTIFY_OUT_REPLY_QR_NOT_SET QR flags set to 0 in reply to notify from %1
 The notify_out library sent a notify message to the namesever at the
 given address, but the reply did not have the QR bit set to one.
 Since there was a response, no more notifies will be sent to this
@@ -75,27 +75,27 @@ explicitly. Please file a bug report. Since there was a response,
 no more notifies will be sent to this server for this notification
 event.
 
-% NOTIFY_OUT_RETRY_EXCEEDED notify to %1#%2: number of retries (%3) exceeded
+% NOTIFY_OUT_RETRY_EXCEEDED notify to %1: number of retries (%2) exceeded
 The maximum number of retries for the notify target has been exceeded.
 Either the address of the secondary nameserver is wrong, or it is not
 responding.
 
-% NOTIFY_OUT_SENDING_NOTIFY sending notify to %1#%2
+% NOTIFY_OUT_SENDING_NOTIFY sending notify to %1
 A notify message is sent to the secondary nameserver at the given
 address.
 
-% NOTIFY_OUT_SOCKET_ERROR socket error sending notify to %1#%2: %3
+% NOTIFY_OUT_SOCKET_ERROR socket error sending notify to %1: %2
 There was a network error while trying to send a notify message to
 the given address. The address might be unreachable. The socket
 error is printed and should provide more information.
 
-% NOTIFY_OUT_SOCKET_RECV_ERROR socket error reading notify reply from %1#%2: %3
+% NOTIFY_OUT_SOCKET_RECV_ERROR socket error reading notify reply from %1: %2
 There was a network error while trying to read a notify reply
 message from the given address. The socket error is printed and should
 provide more information.
 
-% NOTIFY_OUT_TIMEOUT retry notify to %1#%2
-The notify message to the given address (noted as address#port) has
+% NOTIFY_OUT_TIMEOUT retry notify to %1
+The notify message to the given address (noted as address:port) has
 timed out, and the message will be resent until the max retry limit
 is reached.
 
diff --git a/src/lib/python/isc/util/Makefile.am b/src/lib/python/isc/util/Makefile.am
index 3eaaa12..eaeedd8 100644
--- a/src/lib/python/isc/util/Makefile.am
+++ b/src/lib/python/isc/util/Makefile.am
@@ -1,6 +1,7 @@
 SUBDIRS = . cio tests
 
-python_PYTHON = __init__.py process.py socketserver_mixin.py file.py
+python_PYTHON =  __init__.py process.py socketserver_mixin.py file.py
+python_PYTHON += address_formatter.py
 
 pythondir = $(pyexecdir)/isc/util
 
diff --git a/src/lib/python/isc/util/address_formatter.py b/src/lib/python/isc/util/address_formatter.py
new file mode 100644
index 0000000..35c1e00
--- /dev/null
+++ b/src/lib/python/isc/util/address_formatter.py
@@ -0,0 +1,82 @@
+# Copyright (C) 2013  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import socket
+
+class AddressFormatter:
+    """
+    A utility class to convert an IP address with a port number to a
+    string.
+
+    It takes a tuple (or list) containing and address string and a port
+    number, and optionally a family.
+
+    If the family is IPv4, the __str__ output will be
+    <address>:<port>
+    If the family is IPv6, the __str__ output will be
+    [<address>]:<port>
+
+    If family is not given, the __str__ method will try to figure it out
+    itself, by checking for the ':' character in the address string.
+
+    This class is designed to delay the conversion until it's explicitly
+    requested, so the conversion doesn't happen if the corresponding log
+    message is suppressed because of its log level (which is often the case
+    for debug messages).
+
+    Note: this optimization comes with the cost of instantiating the
+    formatter object itself.  It's not really clear which overhead is
+    heavier, and we may conclude it's actually better to just generate
+    the strings unconditionally.  Alternatively, we can make the stored
+    address of this object replaceable so that this object can be reused.
+    Right now this is an open issue.
+
+    See also ClientFormatter in the ddns.logger code, which does something
+    similar but based on other criteria (and optional extra value).
+    """
+    def __init__(self, addr, family=None):
+        self.__addr = addr
+        self.__family = family
+
+    def __addr_v4(self):
+        return self.__addr[0] + ':' + str(self.__addr[1])
+
+    def __addr_v6(self):
+        return '[' + self.__addr[0] + ']:' + str(self.__addr[1])
+
+    def __format_addr(self):
+        # Some basic sanity checks, should we leave this out for efficiency?
+        # (especially strings produce unexpected results)
+        if isinstance(self.__addr, str) or\
+           not hasattr(self.__addr, "__getitem__"):
+            raise ValueError("Address must be a list or tuple")
+
+        if self.__family is not None:
+            if self.__family == socket.AF_INET6:
+                return self.__addr_v6()
+            elif self.__family == socket.AF_INET:
+                return self.__addr_v4()
+            else:
+                raise ValueError("Unknown address family: " +
+                                 str(self.__family))
+        else:
+            if self.__addr[0].find(':') >= 0:
+                return self.__addr_v6()
+            else:
+                return self.__addr_v4()
+
+    def __str__(self):
+        return self.__format_addr()
+
diff --git a/src/lib/python/isc/util/tests/Makefile.am b/src/lib/python/isc/util/tests/Makefile.am
index 3b882b4..e7995d8 100644
--- a/src/lib/python/isc/util/tests/Makefile.am
+++ b/src/lib/python/isc/util/tests/Makefile.am
@@ -1,5 +1,6 @@
 PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
 PYTESTS = process_test.py socketserver_mixin_test.py file_test.py
+PYTESTS += address_formatter_test.py
 EXTRA_DIST = $(PYTESTS)
 
 # If necessary (rare cases), explicitly specify paths to dynamic libraries
@@ -12,7 +13,7 @@ endif
 # test using command-line arguments, so use check-local target instead of TESTS
 check-local:
 if ENABLE_PYTHON_COVERAGE
-	touch $(abs_top_srcdir)/.coverage 
+	touch $(abs_top_srcdir)/.coverage
 	rm -f .coverage
 	${LN_S} $(abs_top_srcdir)/.coverage .coverage
 endif
diff --git a/src/lib/python/isc/util/tests/address_formatter_test.py b/src/lib/python/isc/util/tests/address_formatter_test.py
new file mode 100644
index 0000000..295b7c3
--- /dev/null
+++ b/src/lib/python/isc/util/tests/address_formatter_test.py
@@ -0,0 +1,68 @@
+# Copyright (C) 2013  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import socket
+import unittest
+from isc.util.address_formatter import AddressFormatter
+
+class AddressFormatterTest(unittest.TestCase):
+    def test_v4(self):
+        self.assertEqual("127.0.0.1:123",
+                         str(AddressFormatter(("127.0.0.1", 123))))
+        self.assertEqual("127.0.0.1:123",
+                         str(AddressFormatter(("127.0.0.1", 123), None)))
+        self.assertEqual("192.0.2.1:1",
+                         str(AddressFormatter(("192.0.2.1", 1))))
+
+    def test_v6(self):
+        self.assertEqual("[::1]:123",
+                         str(AddressFormatter(("::1", 123))));
+        self.assertEqual("[::1]:123",
+                         str(AddressFormatter(("::1", 123), None)))
+        self.assertEqual("[2001:db8::]:1",
+                         str(AddressFormatter(("2001:db8::", 1))))
+
+    def test_force_family_good(self):
+        self.assertEqual("127.0.0.1:123",
+                         str(AddressFormatter(("127.0.0.1", 123),
+                                              socket.AF_INET)))
+        self.assertEqual("[::1]:123",
+                         str(AddressFormatter(("::1", 123),
+                                              socket.AF_INET6)))
+
+    def test_force_family_bad(self):
+        """
+        These results are 'bad' as in they don't return the value as
+        specified by our guidelines, since the internal check is skipped if
+        the family is given
+        """
+        self.assertEqual("[127.0.0.1]:123",
+                         str(AddressFormatter(("127.0.0.1", 123),
+                                              socket.AF_INET6)))
+        self.assertEqual("::1:123",
+                         str(AddressFormatter(("::1", 123),
+                                              socket.AF_INET)))
+
+    def test_bad_values(self):
+        self.assertRaises(ValueError, str, AddressFormatter("string"))
+        self.assertRaises(ValueError, str, AddressFormatter(None))
+        self.assertRaises(ValueError, str, AddressFormatter(1))
+        self.assertRaises(ValueError, str, AddressFormatter(("::1", 123), 1))
+        self.assertRaises(ValueError, str, AddressFormatter(("::1", 123), 1))
+
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/src/lib/server_common/client.cc b/src/lib/server_common/client.cc
index e6383d6..3cc2a07 100644
--- a/src/lib/server_common/client.cc
+++ b/src/lib/server_common/client.cc
@@ -57,8 +57,14 @@ Client::getRequestSourceIPAddress() const {
 std::string
 Client::toText() const {
     std::stringstream ss;
-    ss << impl_->request_.getRemoteEndpoint().getAddress().toText()
-       << '#' << impl_->request_.getRemoteEndpoint().getPort();
+    const asiolink::IOAddress& addr =
+        impl_->request_.getRemoteEndpoint().getAddress();
+    if (addr.isV6()) {
+        ss << '[' << addr.toText() << ']';
+    } else {
+        ss << addr.toText();
+    }
+    ss << ':' << impl_->request_.getRemoteEndpoint().getPort();
     return (ss.str());
 }
 
diff --git a/src/lib/server_common/client.h b/src/lib/server_common/client.h
index 912e7a6..4e344f0 100644
--- a/src/lib/server_common/client.h
+++ b/src/lib/server_common/client.h
@@ -119,7 +119,7 @@ public:
     ///
     /// (In the initial implementation) the format of the resulting string
     /// is as follows:
-    /// \code <IP address>#<port>
+    /// \code <IP address>:<port>
     /// \endcode
     /// The IP address is the textual representation of the client's IP
     /// address, which is the source address of the request the client has
diff --git a/src/lib/server_common/portconfig.cc b/src/lib/server_common/portconfig.cc
index b214ef5..122c740 100644
--- a/src/lib/server_common/portconfig.cc
+++ b/src/lib/server_common/portconfig.cc
@@ -117,8 +117,14 @@ installListenAddresses(const AddressList& new_addresses,
     try {
         LOG_DEBUG(logger, DBG_TRACE_BASIC, SRVCOMM_SET_LISTEN);
         BOOST_FOREACH(const AddressPair& addr, new_addresses) {
+            string addr_str;
+            if (addr.first.find(':') != string::npos) {
+                addr_str = "[" + addr.first + "]";
+            } else {
+                addr_str = addr.first;
+            }
             LOG_DEBUG(logger, DBG_TRACE_VALUES, SRVCOMM_ADDRESS_VALUE).
-                arg(addr.first).arg(addr.second);
+                arg(addr_str).arg(addr.second);
         }
         setAddresses(service, new_addresses, server_options);
         address_store = new_addresses;
diff --git a/src/lib/server_common/server_common_messages.mes b/src/lib/server_common/server_common_messages.mes
index 22ce0f3..ba235c9 100644
--- a/src/lib/server_common/server_common_messages.mes
+++ b/src/lib/server_common/server_common_messages.mes
@@ -72,7 +72,7 @@ which it is running.  The server will continue running to allow
 reconfiguration, but will not be listening on any address or port until
 an administrator does so.
 
-% SRVCOMM_ADDRESS_VALUE address to set: %1#%2
+% SRVCOMM_ADDRESS_VALUE address to set: %1:%2
 Debug message. This lists one address and port value of the set of
 addresses we are going to listen on (eg. there will be one log message
 per pair). This appears only after SRVCOMM_SET_LISTEN, but might
diff --git a/src/lib/server_common/tests/client_unittest.cc b/src/lib/server_common/tests/client_unittest.cc
index 08c24ba..14f6fbc 100644
--- a/src/lib/server_common/tests/client_unittest.cc
+++ b/src/lib/server_common/tests/client_unittest.cc
@@ -92,8 +92,8 @@ TEST_F(ClientTest, constructIPv6) {
 }
 
 TEST_F(ClientTest, toText) {
-    EXPECT_EQ("192.0.2.1#53214", client4->toText());
-    EXPECT_EQ("2001:db8::1#53216", client6->toText());
+    EXPECT_EQ("192.0.2.1:53214", client4->toText());
+    EXPECT_EQ("[2001:db8::1]:53216", client6->toText());
 }
 
 // test operator<<.  We simply confirm it appends the result of toText().
diff --git a/tests/lettuce/README b/tests/lettuce/README
index 94bf82b..a53c6e7 100644
--- a/tests/lettuce/README
+++ b/tests/lettuce/README
@@ -70,6 +70,11 @@ These files *will* be overwritten or deleted if the same scenarios are run
 again, so if you want to inspect them after a failed test, either do so
 immediately or move the files.
 
+If you want to keep these output files even for successful runs, you can
+specify the environment variable LETTUCE_KEEP_OUTPUT=1. The files will
+still be overwritten by subsequent runs, but they will not automatically be
+deleted.
+
 Adding and extending tests
 --------------------------
 
diff --git a/tests/lettuce/features/terrain/terrain.py b/tests/lettuce/features/terrain/terrain.py
index 67ca802..2f01723 100644
--- a/tests/lettuce/features/terrain/terrain.py
+++ b/tests/lettuce/features/terrain/terrain.py
@@ -26,12 +26,16 @@
 
 from lettuce import *
 import subprocess
-import os.path
+import os
 import shutil
 import re
 import sys
 import time
 
+# lettuce cannot directly pass commands to the terrain, so we need to
+# use environment variables to influence behaviour
+KEEP_OUTPUT = 'LETTUCE_KEEP_OUTPUT'
+
 # In order to make sure we start all tests with a 'clean' environment,
 # We perform a number of initialization steps, like restoring configuration
 # files, and removing generated data files.
@@ -109,7 +113,7 @@ class RunningProcess:
         self.process = None
         self.step = step
         self.process_name = process_name
-        self.remove_files_on_exit = True
+        self.remove_files_on_exit = (os.environ.get(KEEP_OUTPUT) != '1')
         self._check_output_dir()
         self._create_filenames()
         self._start_process(args)



More information about the bind10-changes mailing list