[svn] commit: r2785 - in /branches/trac311: ./ src/bin/auth/tests/ src/bin/bind10/ src/bin/msgq/ src/bin/xfrin/ src/bin/xfrin/tests/ src/bin/xfrout/tests/ src/lib/bench/ src/lib/cc/ src/lib/cc/tests/ src/lib/config/ src/lib/config/testdata/ src/lib/config/tests/ src/lib/datasrc/ src/lib/dns/ src/lib/dns/python/tests/ src/lib/dns/rdata/generic/ src/lib/dns/tests/ src/lib/dns/util/ src/lib/exceptions/tests/ src/lib/python/isc/cc/ src/lib/python/isc/cc/tests/ src/lib/python/isc/config/ src/lib/python/isc/config/tests/ src/lib/python/isc/notify/tests/
BIND 10 source code commits
bind10-changes at lists.isc.org
Sat Aug 21 01:31:04 UTC 2010
Author: jinmei
Date: Sat Aug 21 01:31:03 2010
New Revision: 2785
Log:
sync with trunk. this revision should be considered the branch point in effect.
Added:
branches/trac311/src/lib/cc/tests/session_unittests_config.h.in
- copied unchanged from r2784, trunk/src/lib/cc/tests/session_unittests_config.h.in
branches/trac311/src/lib/config/testdata/Makefile.am
- copied unchanged from r2784, trunk/src/lib/config/testdata/Makefile.am
branches/trac311/src/lib/config/testdata/b10-config.db.master
- copied unchanged from r2784, trunk/src/lib/config/testdata/b10-config.db.master
Removed:
branches/trac311/src/lib/config/testdata/b10-config.db
Modified:
branches/trac311/ (props changed)
branches/trac311/ChangeLog
branches/trac311/configure.ac
branches/trac311/src/bin/auth/tests/auth_srv_unittest.cc
branches/trac311/src/bin/bind10/run_bind10.sh.in
branches/trac311/src/bin/msgq/msgq.py.in
branches/trac311/src/bin/xfrin/ (props changed)
branches/trac311/src/bin/xfrin/tests/Makefile.am
branches/trac311/src/bin/xfrin/tests/xfrin_test.py
branches/trac311/src/bin/xfrout/tests/Makefile.am
branches/trac311/src/lib/bench/ (props changed)
branches/trac311/src/lib/cc/ (props changed)
branches/trac311/src/lib/cc/session.cc
branches/trac311/src/lib/cc/session.h
branches/trac311/src/lib/cc/tests/ (props changed)
branches/trac311/src/lib/cc/tests/session_unittests.cc
branches/trac311/src/lib/config/Makefile.am
branches/trac311/src/lib/config/ccsession.cc
branches/trac311/src/lib/config/ccsession.h
branches/trac311/src/lib/config/tests/fake_session.h
branches/trac311/src/lib/datasrc/ (props changed)
branches/trac311/src/lib/dns/ (props changed)
branches/trac311/src/lib/dns/python/tests/Makefile.am
branches/trac311/src/lib/dns/rdata/generic/rrsig_46.cc (props changed)
branches/trac311/src/lib/dns/tests/ (props changed)
branches/trac311/src/lib/dns/util/ (props changed)
branches/trac311/src/lib/exceptions/tests/ (props changed)
branches/trac311/src/lib/python/isc/cc/session.py
branches/trac311/src/lib/python/isc/cc/tests/Makefile.am
branches/trac311/src/lib/python/isc/cc/tests/session_test.py
branches/trac311/src/lib/python/isc/config/cfgmgr.py
branches/trac311/src/lib/python/isc/config/tests/Makefile.am
branches/trac311/src/lib/python/isc/config/tests/cfgmgr_test.py
branches/trac311/src/lib/python/isc/notify/tests/Makefile.am
Modified: branches/trac311/ChangeLog
==============================================================================
--- branches/trac311/ChangeLog (original)
+++ branches/trac311/ChangeLog Sat Aug 21 01:31:03 2010
@@ -1,3 +1,21 @@
+ 90. [build] jinmei
+ (Darwin/Mac OS X specific) Specify DYLD_LIBRARY_PATH for tests and
+ experimental run under the source tree. Without this loadable
+ python modules refer to installation paths, which may confuse the
+ operation due to version mismatch or even trigger run time errors
+ due to missing libraries. (Trac #313, r2782)
+
+ 89. [build] jinmei
+ Generate b10-config.db for tests at build time so that the source
+ tree does not have to be writable. (Trac #315, r2776)
+
+ 88. [func] jelte
+ Blocking reads on the msgq command channel now have a timeout
+ (defaults to 4 seconds, modifiable as needed by modules).
+ Because of this, modules will no longer block indefinitely
+ if they are waiting for a message that is not sent for whatever
+ reason. (Trac #296, r2761)
+
87. [func] zhanglikun
lib/python/isc/notifyout: Add the feature of notify-out, when
zone axfr/ixfr finishing, the server will notify its slaves.
Modified: branches/trac311/configure.ac
==============================================================================
--- branches/trac311/configure.ac (original)
+++ branches/trac311/configure.ac Sat Aug 21 01:31:03 2010
@@ -40,14 +40,30 @@
[enable_static_link=yes], [enable_static_link=no])
AM_CONDITIONAL(USE_STATIC_LINK, test $enable_static_link = yes)
-# OS dependent compiler flags
+# OS dependent configuration
+SET_ENV_LIBRARY_PATH=no
+ENV_LIBRARY_PATH=LD_LIBRARY_PATH
+
case "$host" in
*-solaris*)
# Solaris requires special definitions to get some standard libraries
# (e.g. getopt(3)) available with common used header files.
CPPFLAGS="$CPPFLAGS -D_XPG4_2 -D__EXTENSIONS__"
;;
+*-apple-darwin*)
+ # libtool doesn't work pefectly with Darwin: libtool embeds the
+ # final install path in dynamic libraries and our loadable python
+ # modules always refer to that path even if it's loaded within the
+ # source tree. This prevents pre-install tests from working.
+ # To work around this problem we explicitly specify paths to dynamic
+ # libraries when we use them in the source tree.
+ SET_ENV_LIBRARY_PATH=yes
+ ENV_LIBRARY_PATH=DYLD_LIBRARY_PATH
+ ;;
esac
+AM_CONDITIONAL(SET_ENV_LIBRARY_PATH, test $SET_ENV_LIBRARY_PATH = yes)
+AC_SUBST(SET_ENV_LIBRARY_PATH)
+AC_SUBST(ENV_LIBRARY_PATH)
m4_define([_AM_PYTHON_INTERPRETER_LIST], [python python3 python3.1])
AC_ARG_WITH([pythonpath],
@@ -433,6 +449,7 @@
src/lib/python/isc/notify/tests/Makefile
src/lib/config/Makefile
src/lib/config/tests/Makefile
+ src/lib/config/testdata/Makefile
src/lib/dns/Makefile
src/lib/dns/tests/Makefile
src/lib/dns/python/Makefile
@@ -487,6 +504,7 @@
src/lib/python/bind10_config.py
src/lib/dns/tests/testdata/gen-wiredata.py
src/lib/cc/session_config.h.pre
+ src/lib/cc/tests/session_unittests_config.h
], [
chmod +x src/bin/cmdctl/run_b10-cmdctl.sh
chmod +x src/bin/xfrin/run_b10-xfrin.sh
Modified: branches/trac311/src/bin/auth/tests/auth_srv_unittest.cc
==============================================================================
--- branches/trac311/src/bin/auth/tests/auth_srv_unittest.cc (original)
+++ branches/trac311/src/bin/auth/tests/auth_srv_unittest.cc Sat Aug 21 01:31:03 2010
@@ -94,6 +94,8 @@
virtual void startRead(boost::function<void()> read_callback);
virtual int reply(ElementPtr& envelope, ElementPtr& newmsg);
virtual bool hasQueuedMsgs();
+ virtual void setTimeout(size_t timeout UNUSED_PARAM) {};
+ virtual size_t getTimeout() const { return 0; };
void setMessage(ElementPtr msg) { msg_ = msg; }
void disableSend() { send_ok_ = false; }
Modified: branches/trac311/src/bin/bind10/run_bind10.sh.in
==============================================================================
--- branches/trac311/src/bin/bind10/run_bind10.sh.in (original)
+++ branches/trac311/src/bin/bind10/run_bind10.sh.in Sat Aug 21 01:31:03 2010
@@ -24,8 +24,15 @@
export PATH
PYTHONPATH=@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs
-#PYTHONPATH=@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/xfr/.libs
export PYTHONPATH
+
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+SET_ENV_LIBRARY_PATH=@SET_ENV_LIBRARY_PATH@
+if test $SET_ENV_LIBRARY_PATH = yes; then
+ @ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:$@ENV_LIBRARY_PATH@
+ export @ENV_LIBRARY_PATH@
+fi
B10_FROM_SOURCE=@abs_top_srcdir@
export B10_FROM_SOURCE
Modified: branches/trac311/src/bin/msgq/msgq.py.in
==============================================================================
--- branches/trac311/src/bin/msgq/msgq.py.in (original)
+++ branches/trac311/src/bin/msgq/msgq.py.in Sat Aug 21 01:31:03 2010
@@ -212,7 +212,10 @@
EOF."""
received = b''
while len(received) < length:
- data = sock.recv(length - len(received))
+ try:
+ data = sock.recv(length - len(received))
+ except socket.error:
+ raise MsgQReceiveError(socket.error)
if len(data) == 0:
raise MsgQReceiveError("EOF")
received += data
Modified: branches/trac311/src/bin/xfrin/tests/Makefile.am
==============================================================================
--- branches/trac311/src/bin/xfrin/tests/Makefile.am (original)
+++ branches/trac311/src/bin/xfrin/tests/Makefile.am Sat Aug 21 01:31:03 2010
@@ -1,5 +1,12 @@
PYTESTS = xfrin_test.py
EXTRA_DIST = $(PYTESTS)
+
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/xfr/.libs:$$$(ENV_LIBRARY_PATH)
+endif
# later will have configure option to choose this, like: coverage run --branch
PYCOVERAGE = $(PYTHON)
@@ -8,5 +15,6 @@
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
env PYTHONPATH=$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/bin/xfrin:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \
+ $(LIBRARY_PATH_PLACEHOLDER) \
$(PYCOVERAGE) $(abs_srcdir)/$$pytest ; \
done
Modified: branches/trac311/src/bin/xfrin/tests/xfrin_test.py
==============================================================================
--- branches/trac311/src/bin/xfrin/tests/xfrin_test.py (original)
+++ branches/trac311/src/bin/xfrin/tests/xfrin_test.py Sat Aug 21 01:31:03 2010
@@ -559,7 +559,7 @@
def raise_ccerror():
raise isc.cc.session.SessionError('test error')
-def raise_excpetion():
+def raise_exception():
raise Exception('test exception')
class TestMain(unittest.TestCase):
@@ -581,7 +581,7 @@
main(MockXfrin, False)
def test_startup_generalerror(self):
- MockXfrin.check_command_hook = raise_excpetion
+ MockXfrin.check_command_hook = raise_exception
main(MockXfrin, False)
if __name__== "__main__":
Modified: branches/trac311/src/bin/xfrout/tests/Makefile.am
==============================================================================
--- branches/trac311/src/bin/xfrout/tests/Makefile.am (original)
+++ branches/trac311/src/bin/xfrout/tests/Makefile.am Sat Aug 21 01:31:03 2010
@@ -1,5 +1,12 @@
PYTESTS = xfrout_test.py
EXTRA_DIST = $(PYTESTS)
+
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/xfr/.libs:$$$(ENV_LIBRARY_PATH)
+endif
# later will have configure option to choose this, like: coverage run --branch
PYCOVERAGE = $(PYTHON)
@@ -8,5 +15,6 @@
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
env PYTHONPATH=$(abs_top_builddir)/src/bin/xfrout:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/xfr/.libs \
+ $(LIBRARY_PATH_PLACEHOLDER) \
$(PYCOVERAGE) $(abs_srcdir)/$$pytest ; \
done
Modified: branches/trac311/src/lib/cc/session.cc
==============================================================================
--- branches/trac311/src/lib/cc/session.cc (original)
+++ branches/trac311/src/lib/cc/session.cc Sat Aug 21 01:31:03 2010
@@ -28,6 +28,7 @@
#include <unistd.h> // for some IPC/network system calls
#include <asio.hpp>
#include <asio/error_code.hpp>
+#include <asio/deadline_timer.hpp>
#include <asio/system_error.hpp>
#include <cstdio>
@@ -38,7 +39,9 @@
#include <sys/un.h>
#include <boost/bind.hpp>
+#include <boost/optional.hpp>
#include <boost/function.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <exceptions/exceptions.h>
@@ -53,20 +56,39 @@
// (e.g. write(2)) so we don't import the entire asio namespace.
using asio::io_service;
+namespace {
+/// \brief Sets the given Optional 'result' to the given error code
+/// Used as a callback for emulating sync reads with async calls
+/// \param result Pointer to the optional to set
+/// \param err The error code to set it to
+void
+setResult(boost::optional<asio::error_code>* result,
+ const asio::error_code& err)
+{
+ result->reset(err);
+}
+}
+
namespace isc {
namespace cc {
+
class SessionImpl {
public:
SessionImpl(io_service& io_service) :
sequence_(-1), queue_(Element::createList()),
- io_service_(io_service), socket_(io_service_), data_length_(0)
+ io_service_(io_service), socket_(io_service_), data_length_(0),
+ timeout_(MSGQ_DEFAULT_TIMEOUT)
{}
void establish(const char& socket_file);
void disconnect();
void writeData(const void* data, size_t datalen);
size_t readDataLength();
+ // Blocking read. Will throw a SessionTimeout if the timeout value
+ // (in seconds) is thrown. If timeout is 0 it will block forever
void readData(void* data, size_t datalen);
void startRead(boost::function<void()> user_handler);
+ void setTimeout(size_t seconds) { timeout_ = seconds; };
+ size_t getTimeout() const { return timeout_; };
long int sequence_; // the next sequence number to use
std::string lname_;
@@ -82,6 +104,17 @@
uint32_t data_length_;
boost::function<void()> user_handler_;
asio::error_code error_;
+ size_t timeout_;
+
+ // By default, unless changed or disabled, blocking reads on
+ // the msgq channel will time out after 4 seconds in this
+ // implementation.
+ // This number is chosen to be low enough so that whatever
+ // component is blocking does not seem to be hanging, but
+ // still gives enough time for other modules to respond if they
+ // are busy. If this choice turns out to be a bad one, we can
+ // change it later.
+ static const size_t MSGQ_DEFAULT_TIMEOUT = 4000;
};
void
@@ -131,8 +164,51 @@
void
SessionImpl::readData(void* data, size_t datalen) {
+ boost::optional<asio::error_code> read_result;
+ boost::optional<asio::error_code> timer_result;
+
try {
- asio::read(socket_, asio::buffer(data, datalen));
+ asio::async_read(socket_, asio::buffer(data, datalen),
+ boost::bind(&setResult, &read_result, _1));
+ asio::deadline_timer timer(socket_.io_service());
+
+ if (getTimeout() != 0) {
+ timer.expires_from_now(boost::posix_time::milliseconds(getTimeout()));
+ timer.async_wait(boost::bind(&setResult, &timer_result, _1));
+ }
+
+ // wait until either we have read the data we want, the
+ // timer expires, or one of the two is triggered with an error.
+ // When one of them has a result, cancel the other, and wait
+ // until the cancel is processed before we continue
+ while (!read_result && !timer_result) {
+ socket_.io_service().run_one();
+
+ // Don't cancel the timer if we haven't set it
+ if (read_result && getTimeout() != 0) {
+ timer.cancel();
+ while (!timer_result) {
+ socket_.io_service().run_one();
+ }
+ } else if (timer_result) {
+ socket_.cancel();
+ while (!read_result) {
+ socket_.io_service().run_one();
+ }
+ }
+ }
+
+ // asio::error_code evaluates to false if there was no error
+ if (*read_result) {
+ if (*read_result == asio::error::operation_aborted) {
+ isc_throw(SessionTimeout,
+ "Timeout while reading data from cc session");
+ } else {
+ isc_throw(SessionError,
+ "Error while reading data from cc session: " <<
+ read_result->message());
+ }
+ }
} catch (const asio::system_error& asio_ex) {
// to hide ASIO specific exceptions, we catch them explicitly
// and convert it to SessionError.
@@ -144,11 +220,11 @@
SessionImpl::startRead(boost::function<void()> user_handler) {
data_length_ = 0;
user_handler_ = user_handler;
- async_read(socket_, asio::buffer(&data_length_,
- sizeof(data_length_)),
- boost::bind(&SessionImpl::internalRead, this,
- asio::placeholders::error,
- asio::placeholders::bytes_transferred));
+ asio::async_read(socket_, asio::buffer(&data_length_,
+ sizeof(data_length_)),
+ boost::bind(&SessionImpl::internalRead, this,
+ asio::placeholders::error,
+ asio::placeholders::bytes_transferred));
}
void
@@ -410,5 +486,14 @@
return (impl_->queue_->size() > 0);
}
-}
-}
+void
+Session::setTimeout(size_t milliseconds) {
+ impl_->setTimeout(milliseconds);
+}
+
+size_t
+Session::getTimeout() const {
+ return (impl_->getTimeout());
+}
+}
+}
Modified: branches/trac311/src/lib/cc/session.h
==============================================================================
--- branches/trac311/src/lib/cc/session.h (original)
+++ branches/trac311/src/lib/cc/session.h Sat Aug 21 01:31:03 2010
@@ -37,6 +37,15 @@
class SessionError : public isc::Exception {
public:
SessionError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+ };
+
+ /// \brief A standard Exception class that is thrown when a
+ /// blocking readData call does not read the given number of
+ /// bytes before the timeout expires
+ class SessionTimeout : public isc::Exception {
+ public:
+ SessionTimeout(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) {}
};
@@ -88,6 +97,17 @@
virtual int reply(isc::data::ElementPtr& envelope,
isc::data::ElementPtr& newmsg) = 0;
virtual bool hasQueuedMsgs() = 0;
+
+ /// \brief Sets the default timeout for blocking reads
+ /// in this session to the given number of milliseconds
+ /// \param milliseconds the timeout for blocking reads in
+ /// milliseconds, if this is set to 0, reads will block
+ /// forever.
+ virtual void setTimeout(size_t milliseconds) = 0;
+
+ /// \brief Returns the current timeout for blocking reads
+ /// \return The timeout (in milliseconds)
+ virtual size_t getTimeout() const = 0;
};
class Session : public AbstractSession {
@@ -121,6 +141,8 @@
virtual int reply(isc::data::ElementPtr& envelope,
isc::data::ElementPtr& newmsg);
virtual bool hasQueuedMsgs();
+ virtual void setTimeout(size_t milliseconds);
+ virtual size_t getTimeout() const;
private:
void sendmsg(isc::data::ElementPtr& msg);
void sendmsg(isc::data::ElementPtr& env,
Modified: branches/trac311/src/lib/cc/tests/session_unittests.cc
==============================================================================
--- branches/trac311/src/lib/cc/tests/session_unittests.cc (original)
+++ branches/trac311/src/lib/cc/tests/session_unittests.cc Sat Aug 21 01:31:03 2010
@@ -22,10 +22,13 @@
#include <asio.hpp>
#include <gtest/gtest.h>
+#include <boost/bind.hpp>
#include <exceptions/exceptions.h>
#include <cc/session.h>
+#include <cc/data.h>
+#include <session_unittests_config.h>
using namespace isc::cc;
@@ -48,3 +51,190 @@
);
}
+
+// This class sets up a domain socket for the session to connect to
+// it will impersonate the msgq a tiny bit (if setSendLname() has
+// been called, it will send an 'answer' to the lname query that is
+// sent in the initialization of Session objects)
+class TestDomainSocket {
+
+public:
+ TestDomainSocket(asio::io_service& io_service, const char* file) :
+ io_service_(io_service),
+ ep_(file),
+ acceptor_(io_service_, ep_),
+ socket_(io_service_)
+ {
+ acceptor_.async_accept(socket_,
+ boost::bind(&TestDomainSocket::acceptHandler,
+ this, _1));
+ }
+
+ ~TestDomainSocket() {
+ socket_.close();
+ unlink(BIND10_TEST_SOCKET_FILE);
+ }
+
+ void
+ acceptHandler(const asio::error_code& error UNUSED_PARAM) {
+ }
+
+ void
+ sendmsg(isc::data::ElementPtr& env, isc::data::ElementPtr& msg) {
+ const std::string header_wire = env->toWire();
+ const std::string body_wire = msg->toWire();
+ const unsigned int length = 2 + header_wire.length() +
+ body_wire.length();
+ const unsigned int length_net = htonl(length);
+ const unsigned short header_length = header_wire.length();
+ const unsigned short header_length_net = htons(header_length);
+
+ socket_.send(asio::buffer(&length_net, sizeof(length_net)));
+ socket_.send(asio::buffer(&header_length_net,
+ sizeof(header_length_net)));
+ socket_.send(asio::buffer(header_wire.data(), header_length));
+ socket_.send(asio::buffer(body_wire.data(), body_wire.length()));
+ }
+
+ void
+ sendLname() {
+ isc::data::ElementPtr lname_answer1 =
+ isc::data::Element::fromJSON("{ \"type\": \"lname\" }");
+ isc::data::ElementPtr lname_answer2 =
+ isc::data::Element::fromJSON("{ \"lname\": \"foobar\" }");
+ sendmsg(lname_answer1, lname_answer2);
+ }
+
+ void
+ setSendLname() {
+ // ignore whatever data we get, send back an lname
+ asio::async_read(socket_, asio::buffer(data_buf, 0),
+ boost::bind(&TestDomainSocket::sendLname, this));
+ }
+
+private:
+ asio::io_service& io_service_;
+ asio::local::stream_protocol::endpoint ep_;
+ asio::local::stream_protocol::acceptor acceptor_;
+ asio::local::stream_protocol::socket socket_;
+ char data_buf[1024];
+};
+
+class SessionTest : public ::testing::Test {
+protected:
+ SessionTest() : sess(my_io_service), work(my_io_service) {
+ // The TestDomainSocket is held as a 'new'-ed pointer,
+ // so we can call unlink() first.
+ unlink(BIND10_TEST_SOCKET_FILE);
+ tds = new TestDomainSocket(my_io_service, BIND10_TEST_SOCKET_FILE);
+ }
+
+ ~SessionTest() {
+ delete tds;
+ }
+
+public:
+ // used in the handler test
+ // This handler first reads (and ignores) whatever message caused
+ // it to be invoked. Then it calls group_recv for a second message.
+ // If this message is { "command": "stop" } it'll tell the
+ // io_service it is done. Otherwise it'll re-register this handler
+ void someHandler() {
+ isc::data::ElementPtr env, msg;
+ sess.group_recvmsg(env, msg, false, -1);
+
+ sess.group_recvmsg(env, msg, false, -1);
+ if (msg && msg->contains("command") &&
+ msg->get("command")->stringValue() == "stop") {
+ my_io_service.stop();
+ } else {
+ sess.startRead(boost::bind(&SessionTest::someHandler, this));
+ }
+ }
+
+protected:
+ asio::io_service my_io_service;
+ TestDomainSocket* tds;
+ Session sess;
+ // Keep run() from stopping right away by informing it it has work to do
+ asio::io_service::work work;
+};
+
+TEST_F(SessionTest, timeout_on_connect) {
+ // set to a short timeout so the test doesn't take too long
+ EXPECT_EQ(4000, sess.getTimeout());
+ sess.setTimeout(100);
+ EXPECT_EQ(100, sess.getTimeout());
+ // no answer, should timeout
+ EXPECT_THROW(sess.establish(BIND10_TEST_SOCKET_FILE), SessionTimeout);
+}
+
+TEST_F(SessionTest, connect_ok) {
+ tds->setSendLname();
+ sess.establish(BIND10_TEST_SOCKET_FILE);
+}
+
+TEST_F(SessionTest, connect_ok_no_timeout) {
+ tds->setSendLname();
+
+ sess.setTimeout(0);
+ sess.establish(BIND10_TEST_SOCKET_FILE);
+}
+
+TEST_F(SessionTest, connect_ok_connection_reset) {
+ tds->setSendLname();
+
+ sess.establish(BIND10_TEST_SOCKET_FILE);
+ // Close the session again, so the next recv() should throw
+ sess.disconnect();
+
+ isc::data::ElementPtr env, msg;
+ EXPECT_THROW(sess.group_recvmsg(env, msg, false, -1), SessionError);
+}
+
+TEST_F(SessionTest, run_with_handler) {
+ tds->setSendLname();
+
+ sess.establish(BIND10_TEST_SOCKET_FILE);
+ sess.startRead(boost::bind(&SessionTest::someHandler, this));
+
+ isc::data::ElementPtr env = isc::data::Element::fromJSON("{ \"to\": \"me\" }");
+ isc::data::ElementPtr msg = isc::data::Element::fromJSON("{ \"some\": \"message\" }");
+ tds->sendmsg(env, msg);
+
+ msg = isc::data::Element::fromJSON("{ \"another\": \"message\" }");
+ tds->sendmsg(env, msg);
+
+ msg = isc::data::Element::fromJSON("{ \"a third\": \"message\" }");
+ tds->sendmsg(env, msg);
+
+ msg = isc::data::Element::fromJSON("{ \"command\": \"stop\" }");
+ tds->sendmsg(env, msg);
+
+
+ size_t count = my_io_service.run();
+ ASSERT_EQ(2, count);
+}
+
+TEST_F(SessionTest, run_with_handler_timeout) {
+ tds->setSendLname();
+
+ sess.establish(BIND10_TEST_SOCKET_FILE);
+ sess.startRead(boost::bind(&SessionTest::someHandler, this));
+ sess.setTimeout(100);
+
+ isc::data::ElementPtr env = isc::data::Element::fromJSON("{ \"to\": \"me\" }");
+ isc::data::ElementPtr msg = isc::data::Element::fromJSON("{ \"some\": \"message\" }");
+ tds->sendmsg(env, msg);
+
+ msg = isc::data::Element::fromJSON("{ \"another\": \"message\" }");
+ tds->sendmsg(env, msg);
+
+ msg = isc::data::Element::fromJSON("{ \"a third\": \"message\" }");
+ tds->sendmsg(env, msg);
+
+ // No followup message, should time out.
+ ASSERT_THROW(my_io_service.run(), SessionTimeout);
+}
+
+
Modified: branches/trac311/src/lib/config/Makefile.am
==============================================================================
--- branches/trac311/src/lib/config/Makefile.am (original)
+++ branches/trac311/src/lib/config/Makefile.am Sat Aug 21 01:31:03 2010
@@ -1,4 +1,4 @@
-SUBDIRS = . tests
+SUBDIRS = . testdata tests
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
@@ -13,7 +13,7 @@
EXTRA_DIST += testdata/b10-config-bad2.db
EXTRA_DIST += testdata/b10-config-bad3.db
EXTRA_DIST += testdata/b10-config-bad4.db
-EXTRA_DIST += testdata/b10-config.db
+EXTRA_DIST += testdata/b10-config.db.master #.db will be auto-generated
EXTRA_DIST += testdata/data22_1.data
EXTRA_DIST += testdata/data22_2.data
EXTRA_DIST += testdata/data22_3.data
Modified: branches/trac311/src/lib/config/ccsession.cc
==============================================================================
--- branches/trac311/src/lib/config/ccsession.cc (original)
+++ branches/trac311/src/lib/config/ccsession.cc Sat Aug 21 01:31:03 2010
@@ -195,7 +195,7 @@
isc::data::ElementPtr(*config_handler)(isc::data::ElementPtr new_config),
isc::data::ElementPtr(*command_handler)(
const std::string& command, const isc::data::ElementPtr args)
- ) throw (isc::cc::SessionError) :
+ ) :
session_(session)
{
module_specification_ = readModuleSpecification(spec_file_name);
Modified: branches/trac311/src/lib/config/ccsession.h
==============================================================================
--- branches/trac311/src/lib/config/ccsession.h (original)
+++ branches/trac311/src/lib/config/ccsession.h Sat Aug 21 01:31:03 2010
@@ -138,7 +138,7 @@
isc::data::ElementPtr(*command_handler)(
const std::string& command,
const isc::data::ElementPtr args) = NULL
- ) throw (isc::cc::SessionError);
+ );
/**
* Optional optimization for checkCommand loop; returns true
Modified: branches/trac311/src/lib/config/tests/fake_session.h
==============================================================================
--- branches/trac311/src/lib/config/tests/fake_session.h (original)
+++ branches/trac311/src/lib/config/tests/fake_session.h Sat Aug 21 01:31:03 2010
@@ -72,6 +72,8 @@
virtual int reply(isc::data::ElementPtr& envelope,
isc::data::ElementPtr& newmsg);
virtual bool hasQueuedMsgs();
+ virtual void setTimeout(size_t milliseconds) {};
+ virtual size_t getTimeout() const { return 0; };
isc::data::ElementPtr getFirstMessage(std::string& group, std::string& to);
void addMessage(isc::data::ElementPtr, const std::string& group,
const std::string& to);
Modified: branches/trac311/src/lib/dns/python/tests/Makefile.am
==============================================================================
--- branches/trac311/src/lib/dns/python/tests/Makefile.am (original)
+++ branches/trac311/src/lib/dns/python/tests/Makefile.am Sat Aug 21 01:31:03 2010
@@ -10,6 +10,13 @@
EXTRA_DIST = $(PYTESTS)
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+endif
+
# later will have configure option to choose this, like: coverage run --branch
PYCOVERAGE = $(PYTHON)
# test using command-line arguments, so use check-local target instead of TESTS
@@ -18,5 +25,6 @@
echo Running test: $$pytest ; \
env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \
TESTDATA_PATH=$(abs_top_srcdir)/src/lib/dns/tests/testdata \
+ $(LIBRARY_PATH_PLACEHOLDER) \
$(PYCOVERAGE) $(abs_srcdir)/$$pytest ; \
done
Modified: branches/trac311/src/lib/python/isc/cc/session.py
==============================================================================
--- branches/trac311/src/lib/python/isc/cc/session.py (original)
+++ branches/trac311/src/lib/python/isc/cc/session.py Sat Aug 21 01:31:03 2010
@@ -29,6 +29,10 @@
class Session:
def __init__(self, socket_file=None):
self._socket = None
+ # store the current timeout value in seconds (the way
+ # settimeout() wants them, our API takes milliseconds
+ # so that it is consistent with the C++ version)
+ self._socket_timeout = 4;
self._lname = None
self._recvbuffer = bytearray()
self._recvlength = 0
@@ -44,7 +48,6 @@
self.socket_file = bind10_config.BIND10_MSGQ_SOCKET_FILE
else:
self.socket_file = socket_file
-
try:
self._socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
@@ -123,6 +126,10 @@
self._socket.setblocking(0)
else:
self._socket.setblocking(1)
+ if self._socket_timeout == 0.0:
+ self._socket.settimeout(None)
+ else:
+ self._socket.settimeout(self._socket_timeout)
if self._recvlength == 0:
length = 4
@@ -208,6 +215,15 @@
}, isc.cc.message.to_wire(msg))
return seq
+ def set_timeout(self, milliseconds):
+ """Sets the socket timeout for blocking reads to the given
+ number of milliseconds"""
+ self._socket_timeout = milliseconds / 1000.0
+
+ def get_timeout(self):
+ """Returns the current timeout for blocking reads (in milliseconds)"""
+ return self._socket_timeout * 1000.0
+
if __name__ == "__main__":
import doctest
doctest.testmod()
Modified: branches/trac311/src/lib/python/isc/cc/tests/Makefile.am
==============================================================================
--- branches/trac311/src/lib/python/isc/cc/tests/Makefile.am (original)
+++ branches/trac311/src/lib/python/isc/cc/tests/Makefile.am Sat Aug 21 01:31:03 2010
@@ -11,5 +11,6 @@
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \
+ BIND10_TEST_SOCKET_FILE=$(builddir)/test_socket.sock \
$(PYCOVERAGE) $(abs_srcdir)/$$pytest ; \
done
Modified: branches/trac311/src/lib/python/isc/cc/tests/session_test.py
==============================================================================
--- branches/trac311/src/lib/python/isc/cc/tests/session_test.py (original)
+++ branches/trac311/src/lib/python/isc/cc/tests/session_test.py Sat Aug 21 01:31:03 2010
@@ -89,13 +89,20 @@
if msg:
self.recvqueue.extend(msg)
+ def settimeout(self, val):
+ pass
+
+ def gettimeout(self):
+ return 0
+
#
# We subclass the Session class we're testing here, only
# to override the __init__() method, which wants a socket,
# and we need to use our fake socket
class MySession(Session):
- def __init__(self, port=9912):
+ def __init__(self, port=9912, s=None):
self._socket = None
+ self._socket_timeout = 1
self._lname = None
self._recvbuffer = bytearray()
self._recvlength = 0
@@ -104,13 +111,16 @@
self._queue = []
self._lock = threading.RLock()
- try:
- self._socket = MySocket(socket.AF_INET, socket.SOCK_STREAM)
- self._socket.connect(tuple(['127.0.0.1', port]))
- self._lname = "test_name"
- # testing getlname here isn't useful, code removed
- except socket.error as se:
- raise SessionError(se)
+ if s is not None:
+ self._socket = s
+ else:
+ try:
+ self._socket = MySocket(socket.AF_INET, socket.SOCK_STREAM)
+ self._socket.connect(tuple(['127.0.0.1', port]))
+ self._lname = "test_name"
+ # testing getlname here isn't useful, code removed
+ except socket.error as se:
+ raise SessionError(se)
class testSession(unittest.TestCase):
@@ -323,7 +333,27 @@
sess.group_reply({ 'from': 'me', 'group': 'our_group', 'instance': 'other_instance', 'seq': 9}, {"hello": "a"})
sent = sess._socket.readsentmsg();
self.assertEqual(sent, b'\x00\x00\x00\x8b\x00{{"from": "test_name", "seq": 3, "to": "me", "instance": "other_instance", "reply": 9, "group": "our_group", "type": "send"}{"hello": "a"}')
-
+
+ def test_timeout(self):
+ if "BIND10_TEST_SOCKET_FILE" not in os.environ:
+ self.assertEqual("", "This test can only run if the value BIND10_TEST_SOCKET_FILE is set in the environment")
+ TEST_SOCKET_FILE = os.environ["BIND10_TEST_SOCKET_FILE"]
+
+ # create a read domain socket to pass into the session
+ s1 = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ if os.path.exists(TEST_SOCKET_FILE):
+ os.remove(TEST_SOCKET_FILE)
+ s1.bind(TEST_SOCKET_FILE)
+ s1.listen(1)
+
+ s2 = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ s2.connect(TEST_SOCKET_FILE)
+ sess = MySession(1, s2)
+ # set timeout to 100 msec, so test does not take too long
+ sess.set_timeout(100)
+ env, msg = sess.group_recvmsg(False)
+ self.assertEqual(None, env)
+ self.assertEqual(None, msg)
if __name__ == "__main__":
unittest.main()
Modified: branches/trac311/src/lib/python/isc/config/cfgmgr.py
==============================================================================
--- branches/trac311/src/lib/python/isc/config/cfgmgr.py (original)
+++ branches/trac311/src/lib/python/isc/config/cfgmgr.py Sat Aug 21 01:31:03 2010
@@ -395,8 +395,9 @@
self.running = True
while (self.running):
msg, env = self.cc.group_recvmsg(False)
- if msg and not 'result' in msg:
+ # ignore 'None' value (current result of timeout)
+ # and messages that are answers to questions we did
+ # not ask
+ if msg is not None and not 'result' in msg:
answer = self.handle_msg(msg);
self.cc.group_reply(env, answer)
- else:
- self.running = False
Modified: branches/trac311/src/lib/python/isc/config/tests/Makefile.am
==============================================================================
--- branches/trac311/src/lib/python/isc/config/tests/Makefile.am (original)
+++ branches/trac311/src/lib/python/isc/config/tests/Makefile.am Sat Aug 21 01:31:03 2010
@@ -7,9 +7,12 @@
PYCOVERAGE = $(PYTHON)
# test using command-line arguments, so use check-local target instead of TESTS
check-local:
+ ret=0 ; \
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \
CONFIG_TESTDATA_PATH=$(abs_top_srcdir)/src/lib/config/testdata \
- $(PYCOVERAGE) $(abs_srcdir)/$$pytest ; \
- done
+ CONFIG_WR_TESTDATA_PATH=$(abs_top_builddir)/src/lib/config/testdata \
+ $(PYCOVERAGE) $(abs_srcdir)/$$pytest || ret=1; \
+ done; \
+ exit $$ret
Modified: branches/trac311/src/lib/python/isc/config/tests/cfgmgr_test.py
==============================================================================
--- branches/trac311/src/lib/python/isc/config/tests/cfgmgr_test.py (original)
+++ branches/trac311/src/lib/python/isc/config/tests/cfgmgr_test.py Sat Aug 21 01:31:03 2010
@@ -28,19 +28,20 @@
class TestConfigManagerData(unittest.TestCase):
def setUp(self):
self.data_path = os.environ['CONFIG_TESTDATA_PATH']
- self.config_manager_data = ConfigManagerData(self.data_path)
+ self.writable_data_path = os.environ['CONFIG_WR_TESTDATA_PATH']
+ self.config_manager_data = ConfigManagerData(self.writable_data_path)
self.assert_(self.config_manager_data)
def test_init(self):
self.assertEqual(self.config_manager_data.data['version'],
config_data.BIND10_CONFIG_DATA_VERSION)
self.assertEqual(self.config_manager_data.data_path,
- self.data_path)
+ self.writable_data_path)
self.assertEqual(self.config_manager_data.db_filename,
- self.data_path + os.sep + "b10-config.db")
+ self.writable_data_path + os.sep + "b10-config.db")
def test_read_from_file(self):
- ConfigManagerData.read_from_file(self.data_path)
+ ConfigManagerData.read_from_file(self.writable_data_path)
self.assertRaises(ConfigManagerDataEmpty,
ConfigManagerData.read_from_file,
"doesnotexist")
@@ -84,14 +85,15 @@
def setUp(self):
self.data_path = os.environ['CONFIG_TESTDATA_PATH']
+ self.writable_data_path = os.environ['CONFIG_WR_TESTDATA_PATH']
self.fake_session = FakeModuleCCSession()
- self.cm = ConfigManager(self.data_path, self.fake_session)
+ self.cm = ConfigManager(self.writable_data_path, self.fake_session)
self.name = "TestModule"
self.spec = isc.config.module_spec_from_file(self.data_path + os.sep + "/spec2.spec")
def test_init(self):
self.assert_(self.cm.module_specs == {})
- self.assert_(self.cm.data_path == self.data_path)
+ self.assert_(self.cm.data_path == self.writable_data_path)
self.assert_(self.cm.config != None)
self.assert_(self.fake_session.has_subscription("ConfigManager"))
self.assert_(self.fake_session.has_subscription("Boss", "ConfigManager"))
@@ -287,13 +289,14 @@
def test_run(self):
self.fake_session.group_sendmsg({ "command": [ "get_commands_spec" ] }, "ConfigManager")
+ self.fake_session.group_sendmsg({ "command": [ "shutdown" ] }, "ConfigManager")
self.cm.run()
pass
if __name__ == '__main__':
- if not 'CONFIG_TESTDATA_PATH' in os.environ:
- print("You need to set the environment variable CONFIG_TESTDATA_PATH to point to the directory containing the test data files")
+ if not 'CONFIG_TESTDATA_PATH' in os.environ or not 'CONFIG_WR_TESTDATA_PATH' in os.environ:
+ print("You need to set the environment variable CONFIG_TESTDATA_PATH and CONFIG_WR_TESTDATA_PATH to point to the directory containing the test data files")
exit(1)
unittest.main()
Modified: branches/trac311/src/lib/python/isc/notify/tests/Makefile.am
==============================================================================
--- branches/trac311/src/lib/python/isc/notify/tests/Makefile.am (original)
+++ branches/trac311/src/lib/python/isc/notify/tests/Makefile.am Sat Aug 21 01:31:03 2010
@@ -1,5 +1,12 @@
PYTESTS = notify_out_test.py
EXTRA_DIST = $(PYTESTS)
+
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+endif
# later will have configure option to choose this, like: coverage run --branch
PYCOVERAGE = $(PYTHON)
@@ -8,5 +15,6 @@
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \
+ $(LIBRARY_PATH_PLACEHOLDER) \
$(PYCOVERAGE) $(abs_srcdir)/$$pytest ; \
done
More information about the bind10-changes
mailing list