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
{
"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": [
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_;
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_) {
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);
+ }
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_;
// 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;
#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>
}
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()));
+ }
+
}
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
}
}
- 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_);
" \".\": \"" 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
--- /dev/null
- 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
},
"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" },
"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"
"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" },
--- /dev/null
+{
+ "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" }
+ }
+ }
+}