From: Michal 'vorner' Vaner Date: Tue, 24 Jul 2012 09:32:53 +0000 (+0200) Subject: Merge #1976 X-Git-Tag: trac2351_base~97^2~7^2~2^2~21 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0d4685b3e7603585afde1b587cbfefdfaf6a1bb3;p=thirdparty%2Fkea.git Merge #1976 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 --- 0d4685b3e7603585afde1b587cbfefdfaf6a1bb3 diff --cc src/bin/auth/auth.spec.pre.in index 7606be40bc,70fb30e38a..a0b5a3d824 --- a/src/bin/auth/auth.spec.pre.in +++ b/src/bin/auth/auth.spec.pre.in @@@ -125,22 -125,8 +125,18 @@@ { "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": [ diff --cc src/bin/auth/auth_srv.cc index e8176c39e2,a6e59966c2..32d36a507e --- a/src/bin/auth/auth_srv.cc +++ b/src/bin/auth/auth_srv.cc @@@ -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& list) { + if (list) { + impl_->client_lists_[rrclass] = list; + } else { + impl_->client_lists_.erase(rrclass); + } + } + boost::shared_ptr + AuthSrv::getClientList(const RRClass& rrclass) { + return (impl_->getClientList(rrclass)); + } + vector + AuthSrv::getClientListClasses() const { + vector result; + for (map >:: + const_iterator it(impl_->client_lists_.begin()); + it != impl_->client_lists_.end(); ++it) { + result.push_back(it->first); + } + return (result); + } diff --cc src/bin/auth/auth_srv.h index d40a4905e0,3d0c5fb0b8..c1835e60ce --- a/src/bin/auth/auth_srv.h +++ b/src/bin/auth/auth_srv.h @@@ -418,25 -318,32 +318,51 @@@ public void setTSIGKeyRing(const boost::shared_ptr* 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& + 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 + 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 getClientListClasses() const; + private: AuthSrvImpl* impl_; isc::asiolink::SimpleCallback* checkin_; diff --cc src/bin/auth/main.cc index fc3b6b630f,1add334633..0bf24f2849 --- a/src/bin/auth/main.cc +++ b/src/bin/auth/main.cc @@@ -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; diff --cc src/bin/auth/tests/auth_srv_unittest.cc index 0a9efe1e10,1db152772e..1324a4f23e --- a/src/bin/auth/tests/auth_srv_unittest.cc +++ b/src/bin/auth/tests/auth_srv_unittest.cc @@@ -30,10 -30,11 +30,12 @@@ #include #include + #include #include +#include #include #include + #include #include #include @@@ -1627,8 -1667,7 +1670,8 @@@ TEST_F(AuthSrvTest, DDNSForwardPushFail } TEST_F(AuthSrvTest, DDNSForwardClose) { - scoped_ptr tmp_server(new AuthSrv(true, xfrout, ddns_forwarder)); + scoped_ptr 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()); @@@ -1643,85 -1682,36 +1686,117 @@@ EXPECT_FALSE(ddns_forwarder.isConnected()); } +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 tmp_server(new AuthSrv(true, xfrout, ddns_forwarder)); ++ scoped_ptr 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(), + server.getClientList(RRClass::IN())); + // Put something in. + const boost::shared_ptr + list(new isc::datasrc::ConfigurableClientList(RRClass::IN())); + const boost::shared_ptr + 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 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()); + // 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())); + } + } diff --cc src/bin/auth/tests/query_unittest.cc index 4c404e6cfb,81e19e31ce..69d6608c14 --- a/src/bin/auth/tests/query_unittest.cc +++ b/src/bin/auth/tests/query_unittest.cc @@@ -48,6 -49,32 +49,37 @@@ using namespace isc::testutils namespace { + // 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 keeper; + switch (result.code) { + case result::SUCCESS: - return (FindResult(&client_, result.zone_finder, true)); ++ return (FindResult(&client_, result.zone_finder, true, ++ keeper)); + case result::PARTIALMATCH: + if (!exact) { - return (FindResult(&client_, result.zone_finder, false)); ++ 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 diff --cc src/lib/datasrc/client_list.cc index 2ed4f44656,59ed61b233..38ab5cca43 --- a/src/lib/datasrc/client_list.cc +++ b/src/lib/datasrc/client_list.cc @@@ -167,65 -164,42 +174,78 @@@ ConfigurableClientList::configure(cons } } +namespace { + +class CacheKeeper : public ClientList::FindResult::LifeKeeper { +public: + CacheKeeper(const boost::shared_ptr& cache) : + cache_(cache) + {} +private: + const boost::shared_ptr cache_; +}; + +class ContainerKeeper : public ClientList::FindResult::LifeKeeper { +public: + ContainerKeeper(const DataSourceClientContainerPtr& container) : + container_(container) + {} +private: + const DataSourceClientContainerPtr container_; +}; + +boost::shared_ptr - genKeeper(const ConfigurableClientList::DataSourceInfo& info) { - if (info.cache_) { ++genKeeper(const ConfigurableClientList::DataSourceInfo* info) { ++ if (info == NULL) { ++ return (boost::shared_ptr()); ++ } ++ if (info->cache_) { + return (boost::shared_ptr( - new CacheKeeper(info.cache_))); ++ new CacheKeeper(info->cache_))); + } else { + return (boost::shared_ptr( - new ContainerKeeper(info.container_))); ++ 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)); ++ 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 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_); diff --cc src/lib/datasrc/tests/client_list_unittest.cc index 2f5e24d1c6,7e553b95e5..64eb347ea2 --- a/src/lib/datasrc/tests/client_list_unittest.cc +++ b/src/lib/datasrc/tests/client_list_unittest.cc @@@ -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(NULL), + EXPECT_EQ(static_cast(NULL), list_->getDataSources()[0].data_src_client_); // And it can search diff --cc src/lib/python/isc/datasrc/configurableclientlist_python.cc index 5db3a5a8e5,0000000000..81da7d8b9a mode 100644,000000..100644 --- a/src/lib/python/isc/datasrc/configurableclientlist_python.cc +++ b/src/lib/python/isc/datasrc/configurableclientlist_python.cc @@@ -1,308 -1,0 +1,308 @@@ +// 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 + +#include +#include + +#include + +#include +#include + +#include + +#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(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(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(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); ++ 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(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(p)) < 0) { + return (false); + } + Py_INCREF(&configurableclientlist_type); + + return (true); +} + +} // namespace python +} // namespace datasrc +} // namespace isc diff --cc tests/lettuce/configurations/xfrin/inmem_slave.conf index 9b693005da,0e735da29b..cc1c997673 --- a/tests/lettuce/configurations/xfrin/inmem_slave.conf +++ b/tests/lettuce/configurations/xfrin/inmem_slave.conf @@@ -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" }, diff --cc tests/lettuce/configurations/xfrin/retransfer_master.conf index 378cff9977,eae47a6f84..dd29ac893e --- a/tests/lettuce/configurations/xfrin/retransfer_master.conf +++ b/tests/lettuce/configurations/xfrin/retransfer_master.conf @@@ -10,10 -10,10 +10,20 @@@ "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" diff --cc tests/lettuce/configurations/xfrin/retransfer_slave.conf index 260147dfbe,500058042e..cef04cf188 --- a/tests/lettuce/configurations/xfrin/retransfer_slave.conf +++ b/tests/lettuce/configurations/xfrin/retransfer_slave.conf @@@ -10,10 -10,20 +10,20 @@@ "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" }, diff --cc tests/lettuce/configurations/xfrin/retransfer_slave_notify.conf index 57244a49f8,0000000000..d977c5810b mode 100644,000000..100644 --- a/tests/lettuce/configurations/xfrin/retransfer_slave_notify.conf +++ b/tests/lettuce/configurations/xfrin/retransfer_slave_notify.conf @@@ -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" } + } + } +}