]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
Merge #1976
authorMichal 'vorner' Vaner <michal.vaner@nic.cz>
Tue, 24 Jul 2012 09:32:53 +0000 (11:32 +0200)
committerMichal 'vorner' Vaner <michal.vaner@nic.cz>
Tue, 24 Jul 2012 11:18:31 +0000 (13:18 +0200)
Finally! This one was big.

With some minor updates to catch up to the changes after merge.

Conflicts:
src/bin/auth/auth_srv.cc
src/bin/auth/auth_srv.h
src/bin/auth/tests/auth_srv_unittest.cc
src/lib/datasrc/client_list.cc
src/lib/datasrc/tests/client_list_unittest.cc

20 files changed:
1  2 
configure.ac
src/bin/auth/auth.spec.pre.in
src/bin/auth/auth_srv.cc
src/bin/auth/auth_srv.h
src/bin/auth/command.cc
src/bin/auth/main.cc
src/bin/auth/tests/auth_srv_unittest.cc
src/bin/auth/tests/query_unittest.cc
src/bin/ddns/tests/ddns_test.py
src/bin/xfrin/xfrin.py.in
src/lib/config/module_spec.cc
src/lib/datasrc/client_list.cc
src/lib/datasrc/client_list.h
src/lib/datasrc/tests/client_list_unittest.cc
src/lib/python/isc/datasrc/configurableclientlist_python.cc
tests/lettuce/configurations/xfrin/inmem_slave.conf
tests/lettuce/configurations/xfrin/retransfer_master.conf
tests/lettuce/configurations/xfrin/retransfer_slave.conf
tests/lettuce/configurations/xfrin/retransfer_slave_notify.conf
tests/lettuce/features/terrain/bind10_control.py

diff --cc configure.ac
Simple merge
index 7606be40bc633bc2dc941b3e44a7e1b141dcabb1,70fb30e38adbdfdd978d8fc8d9eceb6de89ce65c..a0b5a3d824516ad7847a916fb44f58f740fd8f23
          {
              "item_name": "origin", "item_type": "string",
              "item_optional": false, "item_default": ""
-           },
-         {
-             "item_name": "datasrc", "item_type": "string",
-             "item_optional": true, "item_default": "memory"
            }
          ]
 +      },
 +      {
 +        "command_name": "start_ddns_forwarder",
 +        "command_description": "(Re)start internal forwarding of DDNS Update messages. This is automatically called if b10-ddns is started, and is not expected to be called by administrators; it will be removed as a public command in the future.",
 +        "command_args": []
 +      },
 +      {
 +        "command_name": "stop_ddns_forwarder",
 +        "command_description": "Stop internal forwarding of DDNS Update messages. This is automatically called if b10-ddns is stopped, and is not expected to be called by administrators; it will be removed as a public command in the future.",
 +        "command_args": []
        }
      ],
      "statistics": [
index e8176c39e27069d2b35a5de8f6775ff8f454a869,a6e59966c22098d46712ccc4de442a5e7c86769c..32d36a507e0fbd6d568177890c13ac3a426a638c
@@@ -294,16 -292,7 +301,8 @@@ public
      void resumeServer(isc::asiodns::DNSServer* server,
                        isc::dns::Message& message,
                        bool done);
 +
  private:
-     std::string db_file_;
-     MetaDataSrc data_sources_;
-     /// We keep a pointer to the currently running sqlite datasource
-     /// so that we can specifically remove that one should the database
-     /// file change
-     ConstDataSrcPtr cur_datasrc_;
      bool xfrout_connected_;
      AbstractXfroutClient& xfrout_client_;
  
@@@ -324,22 -315,10 +322,11 @@@ AuthSrvImpl::AuthSrvImpl(AbstractXfrout
      statistics_timer_(io_service_),
      counters_(),
      keyring_(NULL),
 +    ddns_base_forwarder_(ddns_forwarder),
 +    ddns_forwarder_(NULL),
      xfrout_connected_(false),
 -    xfrout_client_(xfrout_client),
 -    ddns_forwarder_("update", ddns_forwarder)
 +    xfrout_client_(xfrout_client)
- {
-     // cur_datasrc_ is automatically initialized by the default constructor,
-     // effectively being an empty (sqlite) data source.  once ccsession is up
-     // the datasource will be set by the configuration setting
-     // add static data source
-     data_sources_.addDataSrc(ConstDataSrcPtr(new StaticDataSrc));
-     // enable or disable the cache
-     cache_.setEnabled(use_cache);
- }
+ {}
  
  AuthSrvImpl::~AuthSrvImpl() {
      if (xfrout_connected_) {
@@@ -1041,19 -907,27 +923,42 @@@ AuthSrv::setTSIGKeyRing(const boost::sh
      impl_->keyring_ = keyring;
  }
  
 +void
 +AuthSrv::createDDNSForwarder() {
 +    LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_START_DDNS_FORWARDER);
 +    impl_->ddns_forwarder_.reset(
 +        new SocketSessionForwarderHolder("update", impl_->ddns_base_forwarder_));
 +}
 +
 +void
 +AuthSrv::destroyDDNSForwarder() {
 +    if (impl_->ddns_forwarder_) {
 +        LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_STOP_DDNS_FORWARDER);
 +        impl_->ddns_forwarder_.reset();
 +    }
 +}
 +
+ void
+ AuthSrv::setClientList(const RRClass& rrclass,
+                        const boost::shared_ptr<ConfigurableClientList>& list) {
+     if (list) {
+         impl_->client_lists_[rrclass] = list;
+     } else {
+         impl_->client_lists_.erase(rrclass);
+     }
+ }
+ boost::shared_ptr<ConfigurableClientList>
+ AuthSrv::getClientList(const RRClass& rrclass) {
+     return (impl_->getClientList(rrclass));
+ }
  
+ vector<RRClass>
+ AuthSrv::getClientListClasses() const {
+     vector<RRClass> result;
+     for (map<RRClass, boost::shared_ptr<ConfigurableClientList> >::
+          const_iterator it(impl_->client_lists_.begin());
+          it != impl_->client_lists_.end(); ++it) {
+         result.push_back(it->first);
+     }
+     return (result);
+ }
index d40a4905e06ffe3273df429f346e1a7892a9353c,3d0c5fb0b895b9959c884bc55122149e14e8a4bb..c1835e60ceff782a6e2643f0f32067a99a33e0ad
@@@ -418,25 -318,32 +318,51 @@@ public
      void setTSIGKeyRing(const boost::shared_ptr<isc::dns::TSIGKeyRing>*
                          keyring);
  
 +    /// \brief Create the internal forwarder for DDNS update messages
 +    ///
 +    /// Until this method is called (it is called when the
 +    /// start_ddns_forwarder command is sent to b10-auth), b10-auth will
 +    /// respond to UPDATE messages with a NOTIMP rcode.
 +    /// If the internal forwarder was already created, it is destroyed and
 +    /// created again. This is useful for instance when b10-ddns is shut
 +    /// down and restarted.
 +    void createDDNSForwarder();
 +
 +    /// \brief Destroy the internal forwarder for DDNS update messages
 +    ///
 +    /// After this method has been called (it is called when the
 +    /// stop_ddns_forwarder command is sent to b10-auth), DDNS Update
 +    /// messages are no longer forwarded internally, but b10-auth will
 +    /// immediately respond with a NOTIMP rcode.
 +    /// If there was no forwarder yet, this method does nothing.
 +    void destroyDDNSForwarder();
 +
+     /// \brief Sets the currently used list for data sources of given
+     ///     class.
+     ///
+     /// Replaces the internally used client list with a new one. Other
+     /// classes are not changed.
+     ///
+     /// \param rrclass The class to modify.
+     /// \param list Shared pointer to the client list. If it is NULL,
+     ///     the list is removed instead.
+     void setClientList(const isc::dns::RRClass& rrclass, const
+                        boost::shared_ptr<isc::datasrc::ConfigurableClientList>&
+                        list);
+     /// \brief Returns the currently used client list for the class.
+     ///
+     /// \param rrclass The class for which to get the list.
+     /// \return The list, or NULL if no list is set for the class.
+     boost::shared_ptr<isc::datasrc::ConfigurableClientList>
+         getClientList(const isc::dns::RRClass& rrclass);
+     /// \brief Returns a list of classes that have a client list.
+     ///
+     /// \return List of classes for which a non-NULL client list
+     ///     has been set by setClientList.
+     std::vector<isc::dns::RRClass> getClientListClasses() const;
  private:
      AuthSrvImpl* impl_;
      isc::asiolink::SimpleCallback* checkin_;
Simple merge
index fc3b6b630fd990e829a29df3ac8006dc8f95404f,1add3346338cdb33a3852e96b3a0344012961967..0bf24f28494c30296e3bb6b20260dd71362a90d0
@@@ -210,14 -214,7 +214,13 @@@ main(int argc, char* argv[]) 
  
          // Successfully initialized.
          LOG_INFO(auth_logger, AUTH_SERVER_STARTED);
 +
 +        // Ping any interested module that (a new) auth is up
 +        // Currently, only the DDNS module is notified, but we could consider
 +        // make an announcement channel for these (one-way) messages
 +        cc_session->group_sendmsg(
 +            isc::config::createCommand(AUTH_STARTED_NOTIFICATION), "DDNS");
          io_service.run();
      } catch (const std::exception& ex) {
          LOG_FATAL(auth_logger, AUTH_SERVER_FAILED).arg(ex.what());
          ret = 1;
index 0a9efe1e10d6e25352fd61d8ff7a799329cdb8a2,1db152772e97184a199b9ca161c98e596b3088e2..1324a4f23ed142c9f3abf4341cb9ca7f8e813889
  #include <server_common/keyring.h>
  
  #include <datasrc/memory_datasrc.h>
+ #include <datasrc/client_list.h>
  #include <auth/auth_srv.h>
 +#include <auth/command.h>
  #include <auth/common.h>
  #include <auth/statistics.h>
+ #include <auth/datasrc_configurator.h>
  
  #include <util/unittests/mock_socketsession.h>
  #include <dns/tests/unittest_util.h>
@@@ -1627,8 -1667,7 +1670,8 @@@ TEST_F(AuthSrvTest, DDNSForwardPushFail
  }
  
  TEST_F(AuthSrvTest, DDNSForwardClose) {
-     scoped_ptr<AuthSrv> tmp_server(new AuthSrv(true, xfrout, ddns_forwarder));
+     scoped_ptr<AuthSrv> tmp_server(new AuthSrv(xfrout, ddns_forwarder));
 +    tmp_server->createDDNSForwarder();
      UnitTestUtil::createRequestMessage(request_message, Opcode::UPDATE(),
                                         default_qid, Name("example.com"),
                                         RRClass::IN(), RRType::SOA());
      EXPECT_FALSE(ddns_forwarder.isConnected());
  }
  
-     scoped_ptr<AuthSrv> tmp_server(new AuthSrv(true, xfrout, ddns_forwarder));
 +namespace {
 +    // Send a basic command without arguments, and check the response has
 +    // result code 0
 +    void sendSimpleCommand(AuthSrv& server, const std::string& command) {
 +        ConstElementPtr response = execAuthServerCommand(server, command,
 +                                                         ConstElementPtr());
 +        int command_result = -1;
 +        isc::config::parseAnswer(command_result, response);
 +        EXPECT_EQ(0, command_result);
 +    }
 +} // end anonymous namespace
 +
 +TEST_F(AuthSrvTest, DDNSForwardCreateDestroy) {
 +    // Test that AuthSrv returns NOTIMP before ddns forwarder is created,
 +    // that the ddns_forwarder is connected when the 'start_ddns_forwarder'
 +    // command has been sent, and that it is no longer connected and auth
 +    // returns NOTIMP after the stop_ddns_forwarding command is sent.
++    scoped_ptr<AuthSrv> tmp_server(new AuthSrv(xfrout, ddns_forwarder));
 +
 +    // Prepare update message to send
 +    UnitTestUtil::createRequestMessage(request_message, Opcode::UPDATE(),
 +                                       default_qid, Name("example.com"),
 +                                       RRClass::IN(), RRType::SOA());
 +    createRequestPacket(request_message, IPPROTO_UDP);
 +
 +    // before creating forwarder. isConnected() should be false and
 +    // rcode to UPDATE should be NOTIMP
 +    parse_message->clear(Message::PARSE);
 +    tmp_server->processMessage(*io_message, *parse_message, *response_obuffer,
 +                               &dnsserv);
 +    EXPECT_FALSE(ddns_forwarder.isConnected());
 +    EXPECT_TRUE(dnsserv.hasAnswer());
 +    headerCheck(*parse_message, default_qid, Rcode::NOTIMP(),
 +                Opcode::UPDATE().getCode(), QR_FLAG, 0, 0, 0, 0);
 +
 +    // now create forwarder
 +    sendSimpleCommand(*tmp_server, "start_ddns_forwarder");
 +
 +    // our mock does not respond, and since auth is supposed to send it on,
 +    // there should now be no result when an UPDATE is sent
 +    parse_message->clear(Message::PARSE);
 +    tmp_server->processMessage(*io_message, *parse_message, *response_obuffer,
 +                               &dnsserv);
 +    EXPECT_FALSE(dnsserv.hasAnswer());
 +    EXPECT_TRUE(ddns_forwarder.isConnected());
 +
 +    // If we send a start again, the connection should be recreated,
 +    // visible because isConnected() reports false until an actual message
 +    // has been forwarded
 +    sendSimpleCommand(*tmp_server, "start_ddns_forwarder");
 +
 +    EXPECT_FALSE(ddns_forwarder.isConnected());
 +    parse_message->clear(Message::PARSE);
 +    tmp_server->processMessage(*io_message, *parse_message, *response_obuffer,
 +                               &dnsserv);
 +    EXPECT_FALSE(dnsserv.hasAnswer());
 +    EXPECT_TRUE(ddns_forwarder.isConnected());
 +
 +    // Now tell it to stop forwarder, should respond with NOTIMP again
 +    sendSimpleCommand(*tmp_server, "stop_ddns_forwarder");
 +
 +    parse_message->clear(Message::PARSE);
 +    tmp_server->processMessage(*io_message, *parse_message, *response_obuffer,
 +                               &dnsserv);
 +    EXPECT_FALSE(ddns_forwarder.isConnected());
 +    EXPECT_TRUE(dnsserv.hasAnswer());
 +    headerCheck(*parse_message, default_qid, Rcode::NOTIMP(),
 +                Opcode::UPDATE().getCode(), QR_FLAG, 0, 0, 0, 0);
 +
 +    // Sending stop again should make no difference
 +    sendSimpleCommand(*tmp_server, "stop_ddns_forwarder");
 +
 +    parse_message->clear(Message::PARSE);
 +    tmp_server->processMessage(*io_message, *parse_message, *response_obuffer,
 +                               &dnsserv);
 +    EXPECT_FALSE(ddns_forwarder.isConnected());
 +    EXPECT_TRUE(dnsserv.hasAnswer());
 +    headerCheck(*parse_message, default_qid, Rcode::NOTIMP(),
 +                Opcode::UPDATE().getCode(), QR_FLAG, 0, 0, 0, 0);
 +}
 +
+ // Check the client list accessors
+ TEST_F(AuthSrvTest, clientList) {
+     // The lists don't exist. Therefore, the list of RRClasses is empty.
+     // We also have no IN list.
+     EXPECT_TRUE(server.getClientListClasses().empty());
+     EXPECT_EQ(boost::shared_ptr<const isc::datasrc::ClientList>(),
+               server.getClientList(RRClass::IN()));
+     // Put something in.
+     const boost::shared_ptr<isc::datasrc::ConfigurableClientList>
+         list(new isc::datasrc::ConfigurableClientList(RRClass::IN()));
+     const boost::shared_ptr<isc::datasrc::ConfigurableClientList>
+         list2(new isc::datasrc::ConfigurableClientList(RRClass::CH()));
+     server.setClientList(RRClass::IN(), list);
+     server.setClientList(RRClass::CH(), list2);
+     // There are two things in the list and they are IN and CH
+     vector<RRClass> classes(server.getClientListClasses());
+     ASSERT_EQ(2, classes.size());
+     EXPECT_EQ(RRClass::IN(), classes[0]);
+     EXPECT_EQ(RRClass::CH(), classes[1]);
+     // And the lists can be retrieved.
+     EXPECT_EQ(list, server.getClientList(RRClass::IN()));
+     EXPECT_EQ(list2, server.getClientList(RRClass::CH()));
+     // Remove one of them
+     server.setClientList(RRClass::CH(),
+         boost::shared_ptr<isc::datasrc::ConfigurableClientList>());
+     // This really got deleted, including the class.
+     classes = server.getClientListClasses();
+     ASSERT_EQ(1, classes.size());
+     EXPECT_EQ(RRClass::IN(), classes[0]);
+     EXPECT_EQ(list, server.getClientList(RRClass::IN()));
+ }
  }
index 4c404e6cfb52a40b56f85a4f5286d4d3de5d2fc4,81e19e31cebec965e461c9c1ee773ca5060cffe8..69d6608c14aa50deef4d1af3be99446125932b8b
@@@ -48,6 -49,32 +49,37 @@@ using namespace isc::testutils
  
  namespace {
  
 -                return (FindResult(&client_, result.zone_finder, true));
+ // Simple wrapper for a sincle data source client.
+ // The list simply delegates all the answers to the single
+ // client.
+ class SingletonList : public ClientList {
+ public:
+     SingletonList(DataSourceClient& client) :
+         client_(client)
+     {}
+     virtual FindResult find(const Name& zone, bool exact, bool) const {
+         DataSourceClient::FindResult result(client_.findZone(zone));
++        // We don't complicate the tests with real life keepers, but we
++        // need to put something to the parameter anyway.
++        const boost::shared_ptr<ClientList::FindResult::LifeKeeper> keeper;
+         switch (result.code) {
+             case result::SUCCESS:
 -                    return (FindResult(&client_, result.zone_finder, false));
++                return (FindResult(&client_, result.zone_finder, true,
++                                   keeper));
+             case result::PARTIALMATCH:
+                 if (!exact) {
++                    return (FindResult(&client_, result.zone_finder, false,
++                                       keeper));
+                 }
+             default:
+                 return (FindResult());
+         }
+     }
+ private:
+     DataSourceClient& client_;
+ };
  // This is the content of the mock zone (see below).
  // It's a sequence of textual RRs that is supposed to be parsed by
  // dns::masterLoad().  Some of the RRs are also used as the expected
Simple merge
Simple merge
Simple merge
index 2ed4f4465689d403dc421fd0bac34e0a8323ba42,59ed61b233ced8efa1a6057111ee48377a2d6e63..38ab5cca432b1074758c1f82121b31d6623e654d
@@@ -167,65 -164,42 +174,78 @@@ ConfigurableClientList::configure(cons
      }
  }
  
- genKeeper(const ConfigurableClientList::DataSourceInfo& info) {
-     if (info.cache_) {
 +namespace {
 +
 +class CacheKeeper : public ClientList::FindResult::LifeKeeper {
 +public:
 +    CacheKeeper(const boost::shared_ptr<InMemoryClient>& cache) :
 +        cache_(cache)
 +    {}
 +private:
 +    const boost::shared_ptr<InMemoryClient> cache_;
 +};
 +
 +class ContainerKeeper : public ClientList::FindResult::LifeKeeper {
 +public:
 +    ContainerKeeper(const DataSourceClientContainerPtr& container) :
 +        container_(container)
 +    {}
 +private:
 +    const DataSourceClientContainerPtr container_;
 +};
 +
 +boost::shared_ptr<ClientList::FindResult::LifeKeeper>
-             new CacheKeeper(info.cache_)));
++genKeeper(const ConfigurableClientList::DataSourceInfo* info) {
++    if (info == NULL) {
++        return (boost::shared_ptr<ClientList::FindResult::LifeKeeper>());
++    }
++    if (info->cache_) {
 +        return (boost::shared_ptr<ClientList::FindResult::LifeKeeper>(
-             new ContainerKeeper(info.container_)));
++            new CacheKeeper(info->cache_)));
 +    } else {
 +        return (boost::shared_ptr<ClientList::FindResult::LifeKeeper>(
 -        return (FindResult(datasrc_client, finder, exact));
++            new ContainerKeeper(info->container_)));
 +    }
 +}
 +
 +}
 +
+ // We have this class as a temporary storage, as the FindResult can't be
+ // assigned.
+ struct ConfigurableClientList::MutableResult {
+     MutableResult() :
+         datasrc_client(NULL),
+         matched_labels(0),
+         matched(false),
+         exact(false),
+         info(NULL)
+     {}
+     DataSourceClient* datasrc_client;
+     ZoneFinderPtr finder;
+     uint8_t matched_labels;
+     bool matched;
+     bool exact;
+     const DataSourceInfo* info;
+     operator FindResult() const {
+         // Conversion to the right result.
++        return (FindResult(datasrc_client, finder, exact, genKeeper(info)));
+     }
+ };
  ClientList::FindResult
  ConfigurableClientList::find(const dns::Name& name, bool want_exact_match,
-                             bool) const
+                              bool want_finder) const
  {
-     // Nothing found yet.
-     //
-     // We have this class as a temporary storage, as the FindResult can't be
-     // assigned.
-     struct MutableResult {
-         MutableResult() :
-             datasrc_client(NULL),
-             matched_labels(0),
-             matched(false)
-         {}
-         DataSourceClient* datasrc_client;
-         ZoneFinderPtr finder;
-         uint8_t matched_labels;
-         bool matched;
-         boost::shared_ptr<FindResult::LifeKeeper> keeper;
-         operator FindResult() const {
-             // Conversion to the right result. If we return this, there was
-             // a partial match at best.
-             return (FindResult(datasrc_client, finder, false, keeper));
-         }
-     } candidate;
+     MutableResult result;
+     findInternal(result, name, want_exact_match, want_finder);
+     return (result);
+ }
  
+ void
+ ConfigurableClientList::findInternal(MutableResult& candidate,
+                                      const dns::Name& name,
+                                      bool want_exact_match, bool) const
+ {
      BOOST_FOREACH(const DataSourceInfo& info, data_sources_) {
          DataSourceClient* client(info.cache_ ? info.cache_.get() :
                                   info.data_src_client_);
Simple merge
index 2f5e24d1c61dc176abc784ff6030cecdcbd1c12a,7e553b95e5131361b93d187f92151c1de43c212d..64eb347ea250e106d374a9c849649d25cbfb5ebc
@@@ -741,10 -762,10 +771,10 @@@ TEST_F(ListTest, masterFiles) 
          "       \".\": \"" TEST_DATA_DIR "/root.zone\""
          "   }"
          "}]"));
-     list_->configure(*elem, true);
+     list_->configure(elem, true);
  
      // It has only the cache
 -    EXPECT_EQ(static_cast<isc::datasrc::DataSourceClient*>(NULL),
 +    EXPECT_EQ(static_cast<const DataSourceClient*>(NULL),
                list_->getDataSources()[0].data_src_client_);
  
      // And it can search
index 5db3a5a8e567efe96fc3da42af9a2b0956725537,0000000000000000000000000000000000000000..81da7d8b9a5c4ab58f639701b65e962cf6afe2ad
mode 100644,000000..100644
--- /dev/null
@@@ -1,308 -1,0 +1,308 @@@
-             self->cppobj->configure(*element, allow_cache);
 +// 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.
 +
 +// Enable this if you use s# variants with PyArg_ParseTuple(), see
 +// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
 +//#define PY_SSIZE_T_CLEAN
 +
 +// Python.h needs to be placed at the head of the program file, see:
 +// http://docs.python.org/py3k/extending/extending.html#a-simple-example
 +#include <Python.h>
 +
 +#include <string>
 +#include <stdexcept>
 +
 +#include <util/python/pycppwrapper_util.h>
 +
 +#include <dns/python/rrclass_python.h>
 +#include <dns/python/name_python.h>
 +
 +#include <datasrc/client_list.h>
 +
 +#include "configurableclientlist_python.h"
 +#include "datasrc.h"
 +#include "finder_python.h"
 +#include "client_python.h"
 +
 +using namespace std;
 +using namespace isc::util::python;
 +using namespace isc::datasrc;
 +using namespace isc::datasrc::python;
 +
 +//
 +// ConfigurableClientList
 +//
 +
 +// Trivial constructor.
 +s_ConfigurableClientList::s_ConfigurableClientList() : cppobj(NULL) {
 +}
 +
 +namespace {
 +
 +int
 +ConfigurableClientList_init(PyObject* po_self, PyObject* args, PyObject*) {
 +    s_ConfigurableClientList* self =
 +        static_cast<s_ConfigurableClientList*>(po_self);
 +    try {
 +        const PyObject* rrclass;
 +        if (PyArg_ParseTuple(args, "O!", &isc::dns::python::rrclass_type,
 +                             &rrclass)) {
 +            self->cppobj =
 +                new ConfigurableClientList(isc::dns::python::
 +                                           PyRRClass_ToRRClass(rrclass));
 +            return (0);
 +        }
 +    } catch (const exception& ex) {
 +        const string ex_what = "Failed to construct ConfigurableClientList object: " +
 +            string(ex.what());
 +        PyErr_SetString(getDataSourceException("Error"), ex_what.c_str());
 +        return (-1);
 +    } catch (...) {
 +        PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception");
 +        return (-1);
 +    }
 +
 +    return (-1);
 +}
 +
 +void
 +ConfigurableClientList_destroy(PyObject* po_self) {
 +    s_ConfigurableClientList* self =
 +        static_cast<s_ConfigurableClientList*>(po_self);
 +    delete self->cppobj;
 +    self->cppobj = NULL;
 +    Py_TYPE(self)->tp_free(self);
 +}
 +
 +PyObject*
 +ConfigurableClientList_configure(PyObject* po_self, PyObject* args) {
 +    s_ConfigurableClientList* self =
 +        static_cast<s_ConfigurableClientList*>(po_self);
 +    try {
 +        const char* configuration;
 +        int allow_cache;
 +        if (PyArg_ParseTuple(args, "si", &configuration, &allow_cache)) {
 +            const isc::data::ConstElementPtr
 +                element(isc::data::Element::fromJSON(string(configuration)));
++            self->cppobj->configure(element, allow_cache);
 +            Py_RETURN_NONE;
 +        } else {
 +            return (NULL);
 +        }
 +    } catch (const isc::data::JSONError& jse) {
 +        const string ex_what(std::string("JSON parse error in data source"
 +                               " configuration: ") + jse.what());
 +        PyErr_SetString(getDataSourceException("Error"), ex_what.c_str());
 +        return (NULL);
 +    } catch (const std::exception& exc) {
 +        PyErr_SetString(getDataSourceException("Error"), exc.what());
 +        return (NULL);
 +    } catch (...) {
 +        PyErr_SetString(getDataSourceException("Error"),
 +                        "Unknown C++ exception");
 +        return (NULL);
 +    }
 +}
 +
 +PyObject*
 +ConfigurableClientList_find(PyObject* po_self, PyObject* args) {
 +    s_ConfigurableClientList* self =
 +        static_cast<s_ConfigurableClientList*>(po_self);
 +    try {
 +        PyObject* name_obj;
 +        int want_exact_match = 0;
 +        int want_finder = 1;
 +        if (PyArg_ParseTuple(args, "O!|ii", &isc::dns::python::name_type,
 +                             &name_obj, &want_exact_match, &want_finder)) {
 +            const isc::dns::Name
 +                name(isc::dns::python::PyName_ToName(name_obj));
 +            const ClientList::FindResult
 +                result(self->cppobj->find(name, want_exact_match,
 +                                          want_finder));
 +            PyObjectContainer dsrc;
 +            if (result.dsrc_client_ == NULL) {
 +                // Use the Py_BuildValue, as it takes care of the
 +                // reference counts correctly.
 +                dsrc.reset(Py_BuildValue(""));
 +            } else {
 +                // Make sure we have a keeper there too, so it doesn't
 +                // die when the underlying client list dies or is
 +                // reconfigured.
 +                //
 +                // However, as it is inside the C++ part, is there a
 +                // reasonable way to test it?
 +                dsrc.reset(wrapDataSourceClient(result.dsrc_client_,
 +                                                result.life_keeper_));
 +            }
 +            PyObjectContainer finder;
 +            if (result.finder_ == NULL) {
 +                finder.reset(Py_BuildValue(""));
 +            } else {
 +                // Make sure it keeps the data source client alive.
 +                finder.reset(createZoneFinderObject(result.finder_,
 +                                                    dsrc.get()));
 +            }
 +            PyObjectContainer exact(PyBool_FromLong(result.exact_match_));
 +
 +            return (Py_BuildValue("OOO", dsrc.get(), finder.get(),
 +                                  exact.get()));
 +        } else {
 +            return (NULL);
 +        }
 +    } catch (const std::exception& exc) {
 +        PyErr_SetString(getDataSourceException("Error"), exc.what());
 +        return (NULL);
 +    } catch (...) {
 +        PyErr_SetString(getDataSourceException("Error"),
 +                        "Unknown C++ exception");
 +        return (NULL);
 +    }
 +}
 +
 +// This list contains the actual set of functions we have in
 +// python. Each entry has
 +// 1. Python method name
 +// 2. Our static function here
 +// 3. Argument type
 +// 4. Documentation
 +PyMethodDef ConfigurableClientList_methods[] = {
 +    { "configure", ConfigurableClientList_configure, METH_VARARGS,
 +        "configure(configuration, allow_cache) -> None\n\
 +\n\
 +Wrapper around C++ ConfigurableClientList::configure\n\
 +\n\
 +This sets the active configuration. It fills the ConfigurableClientList with\
 +corresponding data source clients.\n\
 +\n\
 +If any error is detected, an exception is raised and the previous\
 +configuration preserved.\n\
 +\n\
 +Parameters:\n\
 +  configuration     The configuration, as a JSON encoded string.\
 +  allow_cache       If caching is allowed." },
 +    { "find", ConfigurableClientList_find, METH_VARARGS,
 +"find(zone, want_exact_match=False, want_finder=True) -> datasrc_client,\
 +zone_finder, exact_match\n\
 +\n\
 +Look for a data source containing the given zone.\n\
 +\n\
 +It searches through the contained data sources and returns a data source\
 +containing the zone, the zone finder of the zone and a boolean if the answer\
 +is an exact match.\n\
 +\n\
 +The first parameter is isc.dns.Name object of a name in the zone. If the\
 +want_exact_match is True, only zone with this exact origin is returned.\
 +If it is False, the best matching zone is returned.\n\
 +\n\
 +If the want_finder is False, the returned zone_finder might be None even\
 +if the data source is identified (in such case, the datasrc_client is not\
 +None). Setting it to false allows the client list some optimisations, if\
 +you don't need it, but if you do need it, it is better to set it to True\
 +instead of getting it from the datasrc_client later.\n\
 +\n\
 +If no answer is found, the datasrc_client and zone_finder are None." },
 +    { NULL, NULL, 0, NULL }
 +};
 +
 +const char* const ConfigurableClientList_doc = "\
 +The list of data source clients\n\
 +\n\
 +The purpose is to have several data source clients of the same class\
 +and then be able to search through them to identify the one containing\
 +a given zone.\n\
 +\n\
 +Unlike the C++ version, we don't have the abstract base class. Abstract\
 +classes are not needed due to the duck typing nature of python.\
 +";
 +
 +} // end of unnamed namespace
 +
 +namespace isc {
 +namespace datasrc {
 +namespace python {
 +// This defines the complete type for reflection in python and
 +// parsing of PyObject* to s_ConfigurableClientList
 +// Most of the functions are not actually implemented and NULL here.
 +PyTypeObject configurableclientlist_type = {
 +    PyVarObject_HEAD_INIT(NULL, 0)
 +    "datasrc.ConfigurableClientList",
 +    sizeof(s_ConfigurableClientList),                 // tp_basicsize
 +    0,                                  // tp_itemsize
 +    ConfigurableClientList_destroy,                 // tp_dealloc
 +    NULL,                               // tp_print
 +    NULL,                               // tp_getattr
 +    NULL,                               // tp_setattr
 +    NULL,                               // tp_reserved
 +    NULL,                               // tp_repr
 +    NULL,                               // tp_as_number
 +    NULL,                               // tp_as_sequence
 +    NULL,                               // tp_as_mapping
 +    NULL,                               // tp_hash
 +    NULL,                               // tp_call
 +    NULL,                               // tp_str
 +    NULL,                               // tp_getattro
 +    NULL,                               // tp_setattro
 +    NULL,                               // tp_as_buffer
 +    Py_TPFLAGS_DEFAULT,                 // tp_flags
 +    ConfigurableClientList_doc,
 +    NULL,                               // tp_traverse
 +    NULL,                               // tp_clear
 +    NULL,                               // tp_richcompare
 +    0,                                  // tp_weaklistoffset
 +    NULL,                               // tp_iter
 +    NULL,                               // tp_iternext
 +    ConfigurableClientList_methods,                   // tp_methods
 +    NULL,                               // tp_members
 +    NULL,                               // tp_getset
 +    NULL,                               // tp_base
 +    NULL,                               // tp_dict
 +    NULL,                               // tp_descr_get
 +    NULL,                               // tp_descr_set
 +    0,                                  // tp_dictoffset
 +    ConfigurableClientList_init,                    // tp_init
 +    NULL,                               // tp_alloc
 +    PyType_GenericNew,                  // tp_new
 +    NULL,                               // tp_free
 +    NULL,                               // tp_is_gc
 +    NULL,                               // tp_bases
 +    NULL,                               // tp_mro
 +    NULL,                               // tp_cache
 +    NULL,                               // tp_subclasses
 +    NULL,                               // tp_weaklist
 +    NULL,                               // tp_del
 +    0                                   // tp_version_tag
 +};
 +
 +// Module Initialization, all statics are initialized here
 +bool
 +initModulePart_ConfigurableClientList(PyObject* mod) {
 +    // We initialize the static description object with PyType_Ready(),
 +    // then add it to the module. This is not just a check! (leaving
 +    // this out results in segmentation faults)
 +    if (PyType_Ready(&configurableclientlist_type) < 0) {
 +        return (false);
 +    }
 +    void* p = &configurableclientlist_type;
 +    if (PyModule_AddObject(mod, "ConfigurableClientList",
 +                           static_cast<PyObject*>(p)) < 0) {
 +        return (false);
 +    }
 +    Py_INCREF(&configurableclientlist_type);
 +
 +    return (true);
 +}
 +
 +} // namespace python
 +} // namespace datasrc
 +} // namespace isc
index 9b693005dacdeceb636cabcb90ddcd79bb04a4de,0e735da29b0d42addb1dc91054a7d7b9816b680f..cc1c99767373b0ba581ce35014e5ed334d7b055b
@@@ -9,20 -9,27 +9,27 @@@
      },
      "Auth": {
          "database_file": "data/inmem-xfrin.sqlite3",
-         "datasources": [ {
-             "type": "memory",
-             "class": "IN",
-             "zones": [ {
-                 "origin": "example.org",
-                 "file": "data/inmem-xfrin.sqlite3",
-                 "filetype": "sqlite3"
-             } ]
-         } ],
          "listen_on": [ {
 -            "port": 47806,
 -            "address": "127.0.0.1"
 +            "address": "::1",
 +            "port": 47806
          } ]
      },
+     "data_sources": {
+         "classes": {
+             "IN": [
+                 {
+                     "type": "sqlite3",
+                     "params": {
+                         "database_file": "data/inmem-xfrin.sqlite3"
+                     },
+                     "cache-enable": true,
+                     "cache-zones": [
+                         "example.org"
+                     ]
+                 }
+             ]
+         }
+     },
      "Boss": {
          "components": {
              "b10-auth": { "kind": "needed", "special": "auth" },
index 378cff9977f9d536c5770c792af13fafd0de5dc0,eae47a6f840b2cf29e42eaee8f2243e25bf50c2a..dd29ac893e7fb26979a653db3ff06b68ef3fc92d
      "Auth": {
          "database_file": "data/example.org.sqlite3",
          "listen_on": [ {
 -            "port": 47807,
 -            "address": "::1"
 +            "address": "::1",
 +            "port": 47807
          } ]
      },
++    "data_sources": {
++        "classes": {
++            "IN": [{
++                "type": "sqlite3",
++                "params": {
++                    "database_file": "data/example.org.sqlite3"
++                }
++            }]
++        }
++    },
      "Xfrout": {
          "zone_config": [ {
              "origin": "example.org"
index 260147dfbe06c49ef72f444bd1f511c742fc4d6b,500058042e6471de030aef6faca1c692a12e1f06..cef04cf188f2884ef388fff485e27004e525e28e
      "Auth": {
          "database_file": "data/test_nonexistent_db.sqlite3",
          "listen_on": [ {
 -            "port": 47806,
 -            "address": "127.0.0.1"
 +            "address": "::1",
 +            "port": 47806
          } ]
      },
+     "data_sources": {
+         "classes": {
+             "IN": [{
+                 "type": "sqlite3",
+                 "params": {
+                     "database_file": "data/test_nonexistent_db.sqlite3"
+                 }
+             }]
+         }
+     },
      "Boss": {
          "components": {
              "b10-auth": { "kind": "needed", "special": "auth" },
index 57244a49f8a2cb1494be4c663b22f9b8d4557366,0000000000000000000000000000000000000000..d977c5810be3d3bf85992107f392978ed7bd869e
mode 100644,000000..100644
--- /dev/null
@@@ -1,38 -1,0 +1,48 @@@
 +{
 +    "version": 2,
 +    "Logging": {
 +        "loggers": [ {
 +            "debuglevel": 99,
 +            "severity": "DEBUG",
 +            "name": "*"
 +        } ]
 +    },
 +    "Auth": {
 +        "database_file": "data/xfrin-notify.sqlite3",
 +        "listen_on": [ {
 +            "address": "::1",
 +            "port": 47806
 +        } ]
 +    },
++    "data_sources": {
++        "classes": {
++            "IN": [{
++                "type": "sqlite3",
++                "params": {
++                    "database_file": "data/xfrin-notify.sqlite3"
++                }
++            }]
++        }
++    },
 +    "Xfrin": {
 +        "zones": [ {
 +            "name": "example.org",
 +            "master_addr": "::1",
 +            "master_port": 47807
 +        } ]
 +    },
 +    "Zonemgr": {
 +        "secondary_zones": [ {
 +            "name": "example.org",
 +            "class": "IN"
 +        } ]
 +    },
 +    "Boss": {
 +        "components": {
 +            "b10-auth": { "kind": "needed", "special": "auth" },
 +            "b10-xfrin": { "address": "Xfrin", "kind": "dispensable" },
 +            "b10-zonemgr": { "address": "Zonemgr", "kind": "dispensable" },
 +            "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
 +        }
 +    }
 +}