Michal 'vorner' Vaner 12 years ago
parent
commit
7197d4f537

+ 3 - 4
src/lib/datasrc/Makefile.am

@@ -36,6 +36,7 @@ libdatasrc_la_SOURCES += client.h iterator.h
 libdatasrc_la_SOURCES += database.h database.cc
 libdatasrc_la_SOURCES += factory.h factory.cc
 libdatasrc_la_SOURCES += client_list.h client_list.cc
+libdatasrc_la_SOURCES += memory_datasrc.h memory_datasrc.cc
 nodist_libdatasrc_la_SOURCES = datasrc_messages.h datasrc_messages.cc
 libdatasrc_la_LDFLAGS = -no-undefined -version-info 1:0:1
 
@@ -49,14 +50,12 @@ sqlite3_ds_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
 sqlite3_ds_la_LIBADD += libdatasrc.la
 sqlite3_ds_la_LIBADD += $(SQLITE_LIBS)
 
-memory_ds_la_SOURCES = memory_datasrc.h memory_datasrc.cc
-memory_ds_la_SOURCES += memory_datasrc_link.cc
+memory_ds_la_SOURCES = memory_datasrc_link.cc
 memory_ds_la_LDFLAGS = -module -avoid-version
 memory_ds_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
 memory_ds_la_LIBADD += libdatasrc.la
 
-static_ds_la_SOURCES = memory_datasrc.h memory_datasrc.cc
-static_ds_la_SOURCES += static_datasrc_link.cc
+static_ds_la_SOURCES = static_datasrc_link.cc
 static_ds_la_LDFLAGS = -module -avoid-version
 static_ds_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
 static_ds_la_LIBADD += libdatasrc.la

+ 63 - 13
src/lib/datasrc/client_list.cc

@@ -15,19 +15,32 @@
 #include "client_list.h"
 #include "client.h"
 #include "factory.h"
+#include "memory_datasrc.h"
 
 #include <memory>
 #include <boost/foreach.hpp>
 
 using namespace isc::data;
+using namespace isc::dns;
 using namespace std;
+using namespace boost;
 
 namespace isc {
 namespace datasrc {
 
+ConfigurableClientList::DataSourceInfo::DataSourceInfo(
+    DataSourceClient* data_src_client,
+    const DataSourceClientContainerPtr& container, bool hasCache) :
+    data_src_client_(data_src_client),
+    container_(container)
+{
+    if (hasCache) {
+        cache_.reset(new InMemoryClient);
+    }
+}
+
 void
-ConfigurableClientList::configure(const Element& config, bool) {
-    // TODO: Implement the cache
+ConfigurableClientList::configure(const Element& config, bool allow_cache) {
     // TODO: Implement recycling from the old configuration.
     size_t i(0); // Outside of the try to be able to access it in the catch
     try {
@@ -49,8 +62,48 @@ ConfigurableClientList::configure(const Element& config, bool) {
             // Ask the factory to create the data source for us
             const DataSourcePair ds(this->getDataSourceClient(type,
                                                               paramConf));
+            const bool want_cache(allow_cache &&
+                                  dconf->contains("cache-enable") &&
+                                  dconf->get("cache-enable")->boolValue());
             // And put it into the vector
-            new_data_sources.push_back(DataSourceInfo(ds.first, ds.second));
+            new_data_sources.push_back(DataSourceInfo(ds.first, ds.second,
+                                                      want_cache));
+            if (want_cache) {
+                if (!dconf->contains("cache-zones")) {
+                    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.
+                }
+                const ConstElementPtr zones(dconf->get("cache-zones"));
+                const shared_ptr<InMemoryClient>
+                    cache(new_data_sources.back().cache_);
+                const DataSourceClient* const
+                    client(new_data_sources.back().data_src_client_);
+                for (size_t i(0); i < zones->size(); ++i) {
+                    const Name origin(zones->get(i)->stringValue());
+                    const DataSourceClient::FindResult
+                        zone(client->findZone(origin));
+                    if (zone.code != result::SUCCESS) {
+                        // The data source does not contain the zone, it can't
+                        // be cached.
+                        isc_throw(ConfigurationError, "Unable to cache "
+                                  "non-existent zone " << origin);
+                    }
+                    shared_ptr<InMemoryZoneFinder>
+                        finder(new
+                            InMemoryZoneFinder(zone.zone_finder->getClass(),
+                                               origin));
+                    ZoneIteratorPtr iterator(client->getIterator(origin));
+                    if (!iterator) {
+                        isc_throw(isc::Unexpected, "Got NULL iterator for "
+                                  "zone " << origin);
+                    }
+                    finder->load(*iterator);
+                    cache->addZone(finder);
+                }
+            }
         }
         // If everything is OK up until now, we have the new configuration
         // ready. So just put it there and let the old one die when we exit
@@ -88,14 +141,11 @@ ConfigurableClientList::find(const dns::Name& name, bool want_exact_match,
     } candidate;
 
     BOOST_FOREACH(const DataSourceInfo& info, data_sources_) {
-        // TODO: Once we have support for the caches, consider them too here
-        // somehow. This would probably get replaced by a function, that
-        // checks if there's a cache available, if it is, checks the loaded
-        // zones and zones expected to be in the real data source. If it is
-        // the cached one, provide the cached one. If it is in the external
-        // data source, use the datasource and don't provide the finder yet.
-        const DataSourceClient::FindResult result(
-            info.data_src_client_->findZone(name));
+        DataSourceClient* client(info.cache_ ? info.cache_.get() :
+                                 info.data_src_client_);
+        const DataSourceClient::FindResult result(client->findZone(name));
+        // TODO: Once we mark the zones that are not loaded, but are present
+        // in the data source somehow, check them too.
         switch (result.code) {
             case result::SUCCESS:
                 // If we found an exact match, we have no hope to getting
@@ -103,7 +153,7 @@ ConfigurableClientList::find(const dns::Name& name, bool want_exact_match,
 
                 // TODO: In case we have only the datasource and not the finder
                 // and the need_updater parameter is true, get the zone there.
-                return (FindResult(info.data_src_client_, result.zone_finder,
+                return (FindResult(client, result.zone_finder,
                                    true));
             case result::PARTIALMATCH:
                 if (!want_exact_match) {
@@ -124,7 +174,7 @@ ConfigurableClientList::find(const dns::Name& name, bool want_exact_match,
                     if (labels > candidate.matched_labels ||
                         !candidate.matched) {
                         // This one is strictly better. Replace it.
-                        candidate.datasrc_client = info.data_src_client_;
+                        candidate.datasrc_client = client;
                         candidate.finder = result.zone_finder;
                         candidate.matched_labels = labels;
                         candidate.matched = true;

+ 9 - 4
src/lib/datasrc/client_list.h

@@ -33,6 +33,7 @@ typedef boost::shared_ptr<DataSourceClient> DataSourceClientPtr;
 class DataSourceClientContainer;
 typedef boost::shared_ptr<DataSourceClientContainer>
     DataSourceClientContainerPtr;
+class InMemoryClient;
 
 /// \brief The list of data source clients.
 ///
@@ -212,6 +213,11 @@ public:
     ///     client.
     /// \throw ConfigurationError if the configuration is invalid in some
     ///     sense.
+    /// \throw Unexpected if something misbehaves (like the data source
+    ///     returning NULL iterator).
+    /// \throw NotImplemented if the auto-detection of list of zones is
+    ///     needed.
+    /// \throw Whatever is propagated from within the data source.
     void configure(const data::Element& configuration, bool allow_cache);
 
     /// \brief Implementation of the ClientList::find.
@@ -231,12 +237,11 @@ public:
             data_src_client_(NULL)
         {}
         DataSourceInfo(DataSourceClient* data_src_client,
-                       const DataSourceClientContainerPtr& container) :
-            data_src_client_(data_src_client),
-            container_(container)
-        {}
+                       const DataSourceClientContainerPtr& container,
+                       bool hasCache);
         DataSourceClient* data_src_client_;
         DataSourceClientContainerPtr container_;
+        boost::shared_ptr<InMemoryClient> cache_;
     };
 
     /// \brief The collection of data sources.

+ 0 - 1
src/lib/datasrc/tests/Makefile.am

@@ -67,7 +67,6 @@ run_unittests_SOURCES += client_list_unittest.cc
 # We need the actual module implementation in the tests (they are not part
 # of libdatasrc)
 run_unittests_SOURCES += $(top_srcdir)/src/lib/datasrc/sqlite3_accessor.cc
-run_unittests_SOURCES += $(top_srcdir)/src/lib/datasrc/memory_datasrc.cc
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)

+ 224 - 23
src/lib/datasrc/tests/client_list_unittest.cc

@@ -14,9 +14,13 @@
 
 #include <datasrc/client_list.h>
 #include <datasrc/client.h>
+#include <datasrc/iterator.h>
 #include <datasrc/data_source.h>
+#include <datasrc/memory_datasrc.h>
 
 #include <dns/rrclass.h>
+#include <dns/rrttl.h>
+#include <dns/rdataclass.h>
 
 #include <gtest/gtest.h>
 
@@ -41,7 +45,7 @@ public:
         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");
+            return (RRClass::IN());
         }
         shared_ptr<Context> find(const Name&, const RRType&,
                                  const FindOptions)
@@ -60,6 +64,35 @@ public:
     private:
         Name origin_;
     };
+    class Iterator : public ZoneIterator {
+    public:
+        Iterator(const Name& origin) :
+            origin_(origin),
+            finished_(false),
+            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));
+        }
+        virtual isc::dns::ConstRRsetPtr getNextRRset() {
+            if (finished_) {
+                return (ConstRRsetPtr());
+            } else {
+                finished_ = true;
+                return (soa_);
+            }
+        }
+        virtual isc::dns::ConstRRsetPtr getSOA() const {
+            return (soa_);
+        }
+    private:
+        const Name origin_;
+        bool finished_;
+        const isc::dns::RRsetPtr soa_;
+    };
     // Constructor from a list of zones.
     MockDataSourceClient(const char* zone_names[]) {
         for (const char** zone(zone_names); *zone; ++zone) {
@@ -72,7 +105,13 @@ public:
                          const ConstElementPtr& configuration) :
         type_(type),
         configuration_(configuration)
-    {}
+    {
+        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()));
@@ -103,6 +142,15 @@ public:
     {
         isc_throw(isc::NotImplemented, "Not implemented");
     }
+    virtual ZoneIteratorPtr getIterator(const Name& name, bool) const {
+        if (name == Name("noiter.org")) {
+            isc_throw(isc::NotImplemented, "Asked not to be implemented");
+        } else if (name == Name("null.org")) {
+            return (ZoneIteratorPtr());
+        } else {
+            return (ZoneIteratorPtr(new Iterator(name)));
+        }
+    }
     const string type_;
     const ConstElementPtr configuration_;
 private:
@@ -166,7 +214,6 @@ public:
         config_elem_(Element::fromJSON("["
             "{"
             "   \"type\": \"test_type\","
-            "   \"cache\": \"off\","
             "   \"params\": {}"
             "}]"))
     {
@@ -175,20 +222,28 @@ public:
                 ds(new MockDataSourceClient(ds_zones[i]));
             ds_.push_back(ds);
             ds_info_.push_back(ConfigurableClientList::DataSourceInfo(ds.get(),
-                DataSourceClientContainerPtr()));
+                DataSourceClientContainerPtr(), false));
         }
     }
     // Check the positive result is as we expect it.
     void positiveResult(const ClientList::FindResult& result,
                         const shared_ptr<MockDataSourceClient>& dsrc,
                         const Name& name, bool exact,
-                        const char* test)
+                        const char* test, bool from_cache = false)
     {
         SCOPED_TRACE(test);
-        EXPECT_EQ(dsrc.get(), result.dsrc_client_);
         ASSERT_NE(ZoneFinderPtr(), result.finder_);
         EXPECT_EQ(name, result.finder_->getOrigin());
         EXPECT_EQ(exact, result.exact_match_);
+        if (from_cache) {
+            EXPECT_NE(shared_ptr<InMemoryZoneFinder>(),
+                      dynamic_pointer_cast<InMemoryZoneFinder>(
+                          result.finder_)) << "Finder is not from cache";
+            EXPECT_TRUE(NULL !=
+                        dynamic_cast<InMemoryClient*>(result.dsrc_client_));
+        } else {
+            EXPECT_EQ(dsrc.get(), result.dsrc_client_);
+        }
     }
     // Configure the list with multiple data sources, according to
     // some configuration. It uses the index as parameter, to be able to
@@ -220,7 +275,8 @@ public:
                 FAIL() << "Unknown configuration index " << index;
         }
     }
-    void checkDS(size_t index, const string& type, const string& params) const
+    void checkDS(size_t index, const string& type, const string& params,
+                 bool cache) const
     {
         ASSERT_GT(list_->getDataSources().size(), index);
         MockDataSourceClient* ds(dynamic_cast<MockDataSourceClient*>(
@@ -230,6 +286,8 @@ public:
         ASSERT_NE(ds, static_cast<const MockDataSourceClient*>(NULL));
         EXPECT_EQ(type, ds->type_);
         EXPECT_TRUE(Element::fromJSON(params)->equals(*ds->configuration_));
+        EXPECT_EQ(cache, list_->getDataSources()[index].cache_ !=
+                  shared_ptr<InMemoryClient>());
     }
     shared_ptr<TestedList> list_;
     const ClientList::FindResult negativeResult_;
@@ -349,14 +407,14 @@ TEST_F(ListTest, multiBestMatch) {
 
 // Check the configuration is empty when the list is empty
 TEST_F(ListTest, configureEmpty) {
-    ConstElementPtr elem(new ListElement);
+    const ConstElementPtr elem(new ListElement);
     list_->configure(*elem, true);
     EXPECT_TRUE(list_->getDataSources().empty());
 }
 
 // Check we can get multiple data sources and they are in the right order.
 TEST_F(ListTest, configureMulti) {
-    ConstElementPtr elem(Element::fromJSON("["
+    const ConstElementPtr elem(Element::fromJSON("["
         "{"
         "   \"type\": \"type1\","
         "   \"cache\": \"off\","
@@ -370,8 +428,8 @@ TEST_F(ListTest, configureMulti) {
     ));
     list_->configure(*elem, true);
     EXPECT_EQ(2, list_->getDataSources().size());
-    checkDS(0, "type1", "{}");
-    checkDS(1, "type2", "{}");
+    checkDS(0, "type1", "{}", false);
+    checkDS(1, "type2", "{}", false);
 }
 
 // Check we can pass whatever we want to the params
@@ -396,7 +454,7 @@ TEST_F(ListTest, configureParams) {
             "}]"));
         list_->configure(*elem, true);
         EXPECT_EQ(1, list_->getDataSources().size());
-        checkDS(0, "t", *param);
+        checkDS(0, "t", *param, false);
     }
 }
 
@@ -418,55 +476,198 @@ TEST_F(ListTest, wrongConfig) {
         "[{\"type\": \"test_type\", \"params\": 13}, {\"type\": null}]",
         "[{\"type\": \"test_type\", \"params\": 13}, {\"type\": []}]",
         "[{\"type\": \"test_type\", \"params\": 13}, {\"type\": {}}]",
-        // TODO: Once cache is supported, add some invalid cache values
+        // Bad type of cache-enable
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"x\", \"cache-enable\": 13, \"cache-zones\": []}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"x\", \"cache-enable\": \"xx\", \"cache-zones\": []}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"x\", \"cache-enable\": [], \"cache-zones\": []}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"x\", \"cache-enable\": {}, \"cache-zones\": []}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"x\", \"cache-enable\": null, \"cache-zones\": []}]",
+        // Bad type of cache-zones
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"x\", \"cache-enable\": true, \"cache-zones\": \"x\"}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"x\", \"cache-enable\": true, \"cache-zones\": true}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"x\", \"cache-enable\": true, \"cache-zones\": null}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"x\", \"cache-enable\": true, \"cache-zones\": 13}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"x\", \"cache-enable\": true, \"cache-zones\": {}}]",
         NULL
     };
     // Put something inside to see it survives the exception
     list_->configure(*config_elem_, true);
-    checkDS(0, "test_type", "{}");
+    checkDS(0, "test_type", "{}", false);
     for (const char** config(configs); *config; ++config) {
         SCOPED_TRACE(*config);
         ConstElementPtr elem(Element::fromJSON(*config));
         EXPECT_THROW(list_->configure(*elem, true),
                      ConfigurableClientList::ConfigurationError);
         // Still untouched
-        checkDS(0, "test_type", "{}");
+        checkDS(0, "test_type", "{}", false);
         EXPECT_EQ(1, list_->getDataSources().size());
     }
 }
 
 // The param thing defaults to null. Cache is not used yet.
 TEST_F(ListTest, defaults) {
-    ConstElementPtr elem(Element::fromJSON("["
+    const ConstElementPtr elem(Element::fromJSON("["
         "{"
         "   \"type\": \"type1\""
         "}]"));
     list_->configure(*elem, true);
     EXPECT_EQ(1, list_->getDataSources().size());
-    checkDS(0, "type1", "null");
+    checkDS(0, "type1", "null", false);
 }
 
 // Check we can call the configure multiple times, to change the configuration
 TEST_F(ListTest, reconfigure) {
-    ConstElementPtr empty(new ListElement);
+    const ConstElementPtr empty(new ListElement);
     list_->configure(*config_elem_, true);
-    checkDS(0, "test_type", "{}");
+    checkDS(0, "test_type", "{}", false);
     list_->configure(*empty, true);
     EXPECT_TRUE(list_->getDataSources().empty());
     list_->configure(*config_elem_, true);
-    checkDS(0, "test_type", "{}");
+    checkDS(0, "test_type", "{}", false);
 }
 
 // Make sure the data source error exception from the factory is propagated
 TEST_F(ListTest, dataSrcError) {
-    ConstElementPtr elem(Element::fromJSON("["
+    const ConstElementPtr elem(Element::fromJSON("["
         "{"
         "   \"type\": \"error\""
         "}]"));
     list_->configure(*config_elem_, true);
-    checkDS(0, "test_type", "{}");
+    checkDS(0, "test_type", "{}", false);
     EXPECT_THROW(list_->configure(*elem, true), DataSourceError);
-    checkDS(0, "test_type", "{}");
+    checkDS(0, "test_type", "{}", false);
+}
+
+// Check we can get the cache
+TEST_F(ListTest, configureCacheEmpty) {
+    const ConstElementPtr elem(Element::fromJSON("["
+        "{"
+        "   \"type\": \"type1\","
+        "   \"cache-enable\": true,"
+        "   \"cache-zones\": [],"
+        "   \"params\": {}"
+        "},"
+        "{"
+        "   \"type\": \"type2\","
+        "   \"cache-enable\": false,"
+        "   \"cache-zones\": [],"
+        "   \"params\": {}"
+        "}]"
+    ));
+    list_->configure(*elem, true);
+    EXPECT_EQ(2, list_->getDataSources().size());
+    checkDS(0, "type1", "{}", true);
+    checkDS(1, "type2", "{}", false);
+}
+
+// But no cache if we disallow it globally
+TEST_F(ListTest, configureCacheDisabled) {
+    const ConstElementPtr elem(Element::fromJSON("["
+        "{"
+        "   \"type\": \"type1\","
+        "   \"cache-enable\": true,"
+        "   \"cache-zones\": [],"
+        "   \"params\": {}"
+        "},"
+        "{"
+        "   \"type\": \"type2\","
+        "   \"cache-enable\": false,"
+        "   \"cache-zones\": [],"
+        "   \"params\": {}"
+        "}]"
+    ));
+    list_->configure(*elem, false);
+    EXPECT_EQ(2, list_->getDataSources().size());
+    checkDS(0, "type1", "{}", false);
+    checkDS(1, "type2", "{}", false);
+}
+
+// Put some zones into the cache
+TEST_F(ListTest, cacheZones) {
+    const ConstElementPtr elem(Element::fromJSON("["
+        "{"
+        "   \"type\": \"type1\","
+        "   \"cache-enable\": true,"
+        "   \"cache-zones\": [\"example.org\", \"example.com\"],"
+        "   \"params\": [\"example.org\", \"example.com\", \"exmaple.cz\"]"
+        "}]"));
+    list_->configure(*elem, true);
+    checkDS(0, "type1", "[\"example.org\", \"example.com\", \"exmaple.cz\"]",
+            true);
+
+    const shared_ptr<InMemoryClient> cache(list_->getDataSources()[0].cache_);
+    EXPECT_EQ(2, cache->getZoneCount());
+
+    EXPECT_EQ(result::SUCCESS, cache->findZone(Name("example.org")).code);
+    EXPECT_EQ(result::SUCCESS, cache->findZone(Name("example.com")).code);
+    EXPECT_EQ(result::NOTFOUND, cache->findZone(Name("example.cz")).code);
+
+    // These are cached and answered from the cache
+    positiveResult(list_->find(Name("example.com.")), ds_[0],
+                   Name("example.com."), true, "com", true);
+    positiveResult(list_->find(Name("example.org.")), ds_[0],
+                   Name("example.org."), true, "org", true);
+    positiveResult(list_->find(Name("sub.example.com.")), ds_[0],
+                   Name("example.com."), false, "Subdomain of com", true);
+    // For now, the ones not cached are ignored.
+    EXPECT_TRUE(negativeResult_ == list_->find(Name("example.cz.")));
+}
+
+// Check the caching handles misbehaviour from the data source and
+// misconfiguration gracefully
+TEST_F(ListTest, badCache) {
+    list_->configure(*config_elem_, true);
+    checkDS(0, "test_type", "{}", false);
+    // First, the zone is not in the data source
+    const ConstElementPtr elem1(Element::fromJSON("["
+        "{"
+        "   \"type\": \"type1\","
+        "   \"cache-enable\": true,"
+        "   \"cache-zones\": [\"example.org\"],"
+        "   \"params\": []"
+        "}]"));
+    EXPECT_THROW(list_->configure(*elem1, true),
+                 ConfigurableClientList::ConfigurationError);
+    checkDS(0, "test_type", "{}", false);
+    // Now, the zone doesn't give an iterator
+    const ConstElementPtr elem2(Element::fromJSON("["
+        "{"
+        "   \"type\": \"type1\","
+        "   \"cache-enable\": true,"
+        "   \"cache-zones\": [\"noiter.org\"],"
+        "   \"params\": [\"noiter.org\"]"
+        "}]"));
+    EXPECT_THROW(list_->configure(*elem2, true), isc::NotImplemented);
+    checkDS(0, "test_type", "{}", false);
+    // Now, the zone returns NULL iterator
+    const ConstElementPtr elem3(Element::fromJSON("["
+        "{"
+        "   \"type\": \"type1\","
+        "   \"cache-enable\": true,"
+        "   \"cache-zones\": [\"null.org\"],"
+        "   \"params\": [\"null.org\"]"
+        "}]"));
+    EXPECT_THROW(list_->configure(*elem3, true), isc::Unexpected);
+    checkDS(0, "test_type", "{}", false);
+    // The autodetection of zones is not enabled
+    const ConstElementPtr elem4(Element::fromJSON("["
+        "{"
+        "   \"type\": \"type1\","
+        "   \"cache-enable\": true,"
+        "   \"params\": [\"example.org\"]"
+        "}]"));
+    EXPECT_THROW(list_->configure(*elem4, true), isc::NotImplemented);
+    checkDS(0, "test_type", "{}", false);
 }
 
 }