[svn] commit: r4131 - in /branches/trac448: ./ doc/guide/ src/bin/auth/ src/bin/auth/benchmarks/ src/bin/auth/tests/ src/bin/auth/tests/testdata/ src/bin/bind10/ src/bin/bind10/tests/ src/bin/bindctl/tests/ src/bin/cfgmgr/tests/ src/bin/cmdctl/tests/ src/bin/loadzone/tests/correct/ src/bin/loadzone/tests/error/ src/bin/msgq/tests/ src/bin/recurse/ src/bin/recurse/tests/ src/bin/stats/tests/ src/bin/stats/tests/isc/util/ src/bin/tests/ src/bin/xfrin/tests/ src/bin/xfrout/tests/ src/bin/zonemgr/tests/ src/lib/asiolink/ src/lib/asiolink/tests/ src/lib/datasrc/ src/lib/datasrc/tests/ src/lib/datasrc/tests/testdata/ src/lib/dns/python/tests/ src/lib/log/ src/lib/python/isc/cc/tests/ src/lib/python/isc/config/ src/lib/python/isc/config/tests/ src/lib/python/isc/datasrc/tests/ src/lib/python/isc/log/tests/ src/lib/python/isc/net/tests/ src/lib/python/isc/notify/tests/ src/lib/python/isc/util/tests/ src/lib/python/isc/utils/ src/lib/testutils/ src/lib/testutils/testdata/
BIND 10 source code commits
bind10-changes at lists.isc.org
Mon Jan 3 19:27:42 UTC 2011
Author: jinmei
Date: Mon Jan 3 19:27:41 2011
New Revision: 4131
Log:
sync with trunk
Added:
branches/trac448/src/bin/auth/statistics.cc
- copied unchanged from r4130, trunk/src/bin/auth/statistics.cc
branches/trac448/src/bin/auth/statistics.h
- copied unchanged from r4130, trunk/src/bin/auth/statistics.h
branches/trac448/src/bin/auth/tests/statistics_unittest.cc
- copied unchanged from r4130, trunk/src/bin/auth/tests/statistics_unittest.cc
branches/trac448/src/bin/auth/tests/testdata/ (props changed)
- copied from r4130, trunk/src/bin/auth/tests/testdata/
branches/trac448/src/lib/datasrc/tests/testdata/duplicate_rrset.zone
- copied unchanged from r4130, trunk/src/lib/datasrc/tests/testdata/duplicate_rrset.zone
branches/trac448/src/lib/python/isc/utils/ (props changed)
- copied from r4130, trunk/src/lib/python/isc/utils/
branches/trac448/src/lib/testutils/testdata/example.com.zone
- copied unchanged from r4130, trunk/src/lib/testutils/testdata/example.com.zone
branches/trac448/src/lib/testutils/testdata/example.net.zone
- copied unchanged from r4130, trunk/src/lib/testutils/testdata/example.net.zone
branches/trac448/src/lib/testutils/testdata/example.org.zone
- copied unchanged from r4130, trunk/src/lib/testutils/testdata/example.org.zone
branches/trac448/src/lib/testutils/testdata/example.zone
- copied unchanged from r4130, trunk/src/lib/testutils/testdata/example.zone
branches/trac448/src/lib/testutils/testdata/iquery_fromWire.spec
- copied unchanged from r4130, trunk/src/lib/testutils/testdata/iquery_fromWire.spec
branches/trac448/src/lib/testutils/testdata/iquery_response_fromWire.spec
- copied unchanged from r4130, trunk/src/lib/testutils/testdata/iquery_response_fromWire.spec
Modified:
branches/trac448/ (props changed)
branches/trac448/ChangeLog
branches/trac448/Makefile.am
branches/trac448/README
branches/trac448/configure.ac
branches/trac448/doc/guide/bind10-guide.xml
branches/trac448/src/bin/auth/Makefile.am
branches/trac448/src/bin/auth/auth.spec.pre.in
branches/trac448/src/bin/auth/auth_srv.cc
branches/trac448/src/bin/auth/auth_srv.h
branches/trac448/src/bin/auth/benchmarks/Makefile.am
branches/trac448/src/bin/auth/benchmarks/query_bench.cc
branches/trac448/src/bin/auth/config.cc
branches/trac448/src/bin/auth/main.cc
branches/trac448/src/bin/auth/query.cc
branches/trac448/src/bin/auth/query.h
branches/trac448/src/bin/auth/tests/Makefile.am
branches/trac448/src/bin/auth/tests/auth_srv_unittest.cc
branches/trac448/src/bin/auth/tests/config_unittest.cc
branches/trac448/src/bin/auth/tests/query_unittest.cc
branches/trac448/src/bin/bind10/bind10.py.in (props changed)
branches/trac448/src/bin/bind10/tests/Makefile.am
branches/trac448/src/bin/bindctl/tests/Makefile.am
branches/trac448/src/bin/cfgmgr/tests/Makefile.am
branches/trac448/src/bin/cmdctl/tests/Makefile.am
branches/trac448/src/bin/loadzone/tests/correct/Makefile.am
branches/trac448/src/bin/loadzone/tests/error/Makefile.am
branches/trac448/src/bin/msgq/tests/Makefile.am
branches/trac448/src/bin/recurse/b10-recurse.8
branches/trac448/src/bin/recurse/b10-recurse.xml
branches/trac448/src/bin/recurse/tests/ (props changed)
branches/trac448/src/bin/stats/tests/Makefile.am
branches/trac448/src/bin/stats/tests/isc/util/ (props changed)
branches/trac448/src/bin/tests/Makefile.am
branches/trac448/src/bin/xfrin/tests/Makefile.am
branches/trac448/src/bin/xfrout/tests/Makefile.am
branches/trac448/src/bin/zonemgr/tests/Makefile.am
branches/trac448/src/lib/asiolink/ (props changed)
branches/trac448/src/lib/asiolink/asiolink.cc
branches/trac448/src/lib/asiolink/asiolink.h
branches/trac448/src/lib/asiolink/tests/asiolink_unittest.cc
branches/trac448/src/lib/datasrc/memory_datasrc.cc
branches/trac448/src/lib/datasrc/memory_datasrc.h
branches/trac448/src/lib/datasrc/rbtree.h
branches/trac448/src/lib/datasrc/tests/memory_datasrc_unittest.cc
branches/trac448/src/lib/datasrc/tests/rbtree_unittest.cc
branches/trac448/src/lib/datasrc/zone.h
branches/trac448/src/lib/datasrc/zonetable.cc
branches/trac448/src/lib/datasrc/zonetable.h
branches/trac448/src/lib/dns/python/tests/Makefile.am
branches/trac448/src/lib/log/ (props changed)
branches/trac448/src/lib/python/isc/cc/tests/Makefile.am
branches/trac448/src/lib/python/isc/config/module_spec.py
branches/trac448/src/lib/python/isc/config/tests/Makefile.am
branches/trac448/src/lib/python/isc/config/tests/module_spec_test.py
branches/trac448/src/lib/python/isc/datasrc/tests/Makefile.am
branches/trac448/src/lib/python/isc/log/tests/Makefile.am
branches/trac448/src/lib/python/isc/net/tests/Makefile.am
branches/trac448/src/lib/python/isc/notify/tests/Makefile.am
branches/trac448/src/lib/python/isc/util/tests/Makefile.am
branches/trac448/src/lib/testutils/ (props changed)
branches/trac448/src/lib/testutils/testdata/Makefile.am
Modified: branches/trac448/ChangeLog
==============================================================================
--- branches/trac448/ChangeLog (original)
+++ branches/trac448/ChangeLog Mon Jan 3 19:27:41 2011
@@ -1,3 +1,33 @@
+ 142. [func] jinmei
+ b10-auth: updated query benchmark so that it can test in memory
+ data source. Also fixed a bug that the output buffer isn't
+ cleared after query processing, resulting in misleading results
+ or program crash. This is a regression due to change #135.
+ (Trac #465, svn r4103)
+
+ 141. [bug] jinmei
+ b10-auth: Fixed a bug that the authoritative server includes
+ trailing garbage data in responses. This is a regression due to
+ change #135. (Trac #462, svn r4081)
+
+ 140. [func] y-aharen
+ src/bin/auth: Added a feature to count queries and send counter
+ values to statistics periodically. To support it, added wrapping
+ class of asio::deadline_timer to use as interval timer.
+ The counters can be seen using the "Stats show" command from
+ bindctl. The result would look like:
+ ... "auth.queries.tcp": 1, "auth.queries.udp": 1 ...
+ Using the "Auth sendstats" command you can make b10-auth send the
+ counters to b10-stats immediately.
+ (Trac #347, svn r4026)
+
+ 139. [build] jreed
+ Introduced configure option and make targets for generating
+ Python code coverage report. This adds new make targets:
+ report-python-coverage and clean-python-coverage. The C++
+ code coverage targets were renamed to clean-cpp-coverage
+ and report-cpp-coverage. (Trac #362, svn r4023)
+
138. [func]* jinmei
b10-auth: added a configuration interface to support in memory
data sources. For example, the following command to bindctl
Modified: branches/trac448/Makefile.am
==============================================================================
--- branches/trac448/Makefile.am (original)
+++ branches/trac448/Makefile.am Mon Jan 3 19:27:41 2011
@@ -8,20 +8,30 @@
# When running distcheck target, do not install the configurations
DISTCHECK_CONFIGURE_FLAGS = --disable-install-configurations
-clean-coverage:
+clean-cpp-coverage:
@if [ $(USE_LCOV) = yes ] ; then \
$(LCOV) --directory . --zerocounters; \
rm -rf coverage/; \
else \
- echo "Code coverage not enabled at configuration time"; \
- exit 1; \
+ echo "C++ code coverage not enabled at configuration time." ; \
+ echo "Use: ./configure --with-lcov" ; \
+ fi
+
+clean-python-coverage:
+ @if [ $(USE_PYCOVERAGE) = yes ] ; then \
+ rm -f $(abs_top_srcdir)/.coverage ; \
+ rm -rf $(abs_top_srcdir)/py-coverage-html ; \
+ else \
+ echo "Python code coverage not enabled at configuration time." ; \
+ echo "Use: ./configure --with-pycoverage" ; \
fi
perform-coverage: check
-report-coverage:
- $(LCOV) --capture --directory . --output-file all.info
- $(LCOV) --remove all.info \
+report-cpp-coverage:
+ @if [ $(USE_LCOV) = yes ] ; then \
+ $(LCOV) --capture --directory . --output-file all.info ; \
+ $(LCOV) --remove all.info \
c++/4.4\*/\* \
c++/4.4\*/backward/\* \
c++/4.4\*/bits/\* \
@@ -36,10 +46,29 @@
\*_unittests.cc \
\*_unittest.cc \
\*_unittests.h \
- --output report.info
- $(GENHTML) --legend -o coverage report.info
-
+ --output report.info ; \
+ $(GENHTML) --legend -o $(abs_top_builddir)/coverage-cpp-html report.info ; \
+ echo "Generated C++ Code Coverage report in HTML at $(abs_top_builddir)/coverage-cpp-html" ; \
+ else \
+ echo "C++ code coverage not enabled at configuration time." ; \
+ echo "Use: ./configure --with-lcov" ; \
+ fi
+
+report-python-coverage:
+ @if [ $(USE_PYCOVERAGE) = yes ] ; then \
+ $(PYCOVERAGE) html -d $(abs_top_builddir)/coverage-python-html --omit=src/bin/bind10/tests/,src/bin/bindctl/tests/,src/bin/cfgmgr/tests/,src/bin/cmdctl/tests/,src/bin/loadzone/tests/,src/bin/msgq/tests/,src/bin/stats/tests/,src/bin/tests/,src/bin/xfrin/tests/,src/bin/xfrout/tests/,src/bin/zonemgr/tests/,src/lib/dns/python/tests/,src/lib/dns/tests/,src/lib/python/isc/cc/tests/,src/lib/python/isc/config/tests/,src/lib/python/isc/datasrc/tests/,src/lib/python/isc/log/tests/,src/lib/python/isc/net/tests/,src/lib/python/isc/notify/tests/,src/lib/python/isc/util/tests/ ; \
+ echo "Generated Python Code Coverage report in HTML at $(abs_top_builddir)/coverage-python-html" ; \
+ else \
+ echo "Python code coverage not enabled at configuration time." ; \
+ echo "Use: ./configure --with-pycoverage" ; \
+ fi
+
+# for python and c++ test coverage
coverage: clean-coverage perform-coverage report-coverage
+
+clean-coverage: clean-cpp-coverage clean-python-coverage
+
+report-coverage: report-cpp-coverage report-python-coverage
#### include external sources in the distributed tarball:
EXTRA_DIST = ext/asio/README
Modified: branches/trac448/README
==============================================================================
--- branches/trac448/README (original)
+++ branches/trac448/README Mon Jan 3 19:27:41 2011
@@ -15,11 +15,11 @@
This release includes the bind10 master process, b10-msgq message
bus, b10-auth authoritative DNS server (with SQLite3 backend),
-b10-cmdctl remote control daemon, b10-cfgmgr configuration manager,
-b10-xfrin AXFR inbound service, b10-xfrout outgoing AXFR service,
-b10-zonemgr secondary manager, b10-stats statistics collection and
-reporting daemon, and a new libdns++ library for C++ with a python
-wrapper.
+b10-recurse forwarding DNS server, b10-cmdctl remote control daemon,
+b10-cfgmgr configuration manager, b10-xfrin AXFR inbound service,
+b10-xfrout outgoing AXFR service, b10-zonemgr secondary manager,
+b10-stats statistics collection and reporting daemon, and a new
+libdns++ library for C++ with a python wrapper.
Documentation is included and also available via the BIND 10
website at http://bind10.isc.org/
@@ -93,26 +93,55 @@
TEST COVERAGE
+Code coverage reports may be generated using make. These are
+based on running on the unit tests. The resulting reports are placed
+in coverage-cpp-html and coverage-python-html directories for C++
+and Python, respectively.
+
The code coverage report for the C++ tests uses LCOV. It is available
-from http://ltp.sourceforge.net/. To generate your own HTML report,
+from http://ltp.sourceforge.net/. To generate the HTML report,
first configure BIND 10 with:
./configure --with-lcov
+The code coverage report for the Python tests uses coverage.py (aka
+pycoverage). It is available from http://nedbatchelder.com/code/coverage/.
+To generate the HTML report, first configure BIND 10 with:
+
+ ./configure --with-pycoverage
+
Doing code coverage tests:
make coverage
- Does the following:
+ Does the clean, perform, and report targets for C++ and Python.
make clean-coverage
- Zeroes the lcov code coverage counters and removes the coverage HTML.
+ Zeroes the code coverage counters and removes the HTML reports
+ for C++ and Python.
make perform-coverage
- Runs the C++ tests (using googletests framework).
+ Runs the C++ (using the googletests framework) and Python
+ tests.
make report-coverage
- Generates the coverage HTML, excluding some unrelated headers.
- The HTML reports are placed in a directory called coverage/.
+ Generates the coverage reports in HTML for C++ and Python.
+
+ make clean-cpp-coverage
+ Zeroes the code coverage counters and removes the HTML report
+ for the C++ tests.
+
+ make clean-python-coverage
+ Zeroes the code coverage counters and removes the HTML report
+ for the Python tests.
+
+ make report-cpp-coverage
+ Generates the coverage report in HTML for C++, excluding
+ some unrelated headers. The HTML reports are placed in a
+ directory called coverage-cpp-html/.
+
+ make report-python-coverage
+ Generates the coverage report in HTML for Python. The HTML
+ reports are placed in a directory called coverage-python-html/.
DEVELOPERS
Modified: branches/trac448/configure.ac
==============================================================================
--- branches/trac448/configure.ac (original)
+++ branches/trac448/configure.ac Mon Jan 3 19:27:41 2011
@@ -287,6 +287,26 @@
AC_DEFINE(HAVE_SA_LEN, 1, [Define to 1 if sockaddr has a sa_len member, and corresponding sin_len and sun_len])],
AC_MSG_RESULT(no))
+AC_ARG_WITH(pycoverage,
+[ --with-pycoverage[=PROGRAM] enable python code coverage using the specified coverage], pycoverage="$withval", pycoverage="no")
+if test "$pycoverage" = "no" ; then
+ # just run the tests normally with python
+ PYCOVERAGE_RUN="${PYTHON}"
+ USE_PYCOVERAGE="no"
+elif test "$pycoverage" = "yes" ; then
+ PYCOVERAGE="coverage"
+ PYCOVERAGE_RUN="${PYCOVERAGE} run --branch --append"
+ USE_PYCOVERAGE="yes"
+else
+ PYCOVERAGE="$pycoverage"
+ PYCOVERAGE_RUN="${PYCOVERAGE} run --branch --append"
+ USE_PYCOVERAGE="yes"
+fi
+AM_CONDITIONAL(ENABLE_PYTHON_COVERAGE, test x$USE_PYCOVERAGE != xno)
+AC_SUBST(PYCOVERAGE)
+AC_SUBST(PYCOVERAGE_RUN)
+AC_SUBST(USE_PYCOVERAGE)
+
AC_ARG_WITH(lcov,
[ --with-lcov[=PROGRAM] enable gtest and coverage target using the specified lcov], lcov="$withval", lcov="no")
@@ -351,7 +371,7 @@
BOOST_INCLUDES="-I${boost_include_path}"
CPPFLAGS="$CPPFLAGS $BOOST_INCLUDES"
fi
-AC_CHECK_HEADERS([boost/shared_ptr.hpp boost/foreach.hpp boost/interprocess/sync/interprocess_upgradable_mutex.hpp],,
+AC_CHECK_HEADERS([boost/shared_ptr.hpp boost/foreach.hpp boost/interprocess/sync/interprocess_upgradable_mutex.hpp boost/date_time/posix_time/posix_time_types.hpp boost/bind.hpp boost/function.hpp],,
AC_MSG_ERROR([Missing required header files.]))
CPPFLAGS="$CPPFLAGS_SAVES"
AC_SUBST(BOOST_INCLUDES)
@@ -731,7 +751,8 @@
Developer:
Google Tests: $gtest_path
- Code Coverage: $USE_LCOV
+ C++ Code Coverage: $USE_LCOV
+ Python Code Coverage: $USE_PYCOVERAGE
Generate Manuals: $enable_man
END
Modified: branches/trac448/doc/guide/bind10-guide.xml
==============================================================================
--- branches/trac448/doc/guide/bind10-guide.xml (original)
+++ branches/trac448/doc/guide/bind10-guide.xml Mon Jan 3 19:27:41 2011
@@ -42,9 +42,8 @@
<note>
<para>
- BIND 10, at this time, does not provide a recursive
- DNS server. It does provide a EDNS0- and DNSSEC-capable
- authoritative DNS server.
+ BIND 10 provides a EDNS0- and DNSSEC-capable
+ authoritative DNS server and a forwarding DNS server.
</para>
</note>
Modified: branches/trac448/src/bin/auth/Makefile.am
==============================================================================
--- branches/trac448/src/bin/auth/Makefile.am (original)
+++ branches/trac448/src/bin/auth/Makefile.am Mon Jan 3 19:27:41 2011
@@ -41,6 +41,7 @@
b10_auth_SOURCES += change_user.cc change_user.h
b10_auth_SOURCES += config.cc config.h
b10_auth_SOURCES += common.h
+b10_auth_SOURCES += statistics.cc statistics.h
b10_auth_SOURCES += main.cc
b10_auth_LDADD = $(top_builddir)/src/lib/datasrc/libdatasrc.la
b10_auth_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
Modified: branches/trac448/src/bin/auth/auth.spec.pre.in
==============================================================================
--- branches/trac448/src/bin/auth/auth.spec.pre.in (original)
+++ branches/trac448/src/bin/auth/auth.spec.pre.in Mon Jan 3 19:27:41 2011
@@ -59,6 +59,11 @@
"command_name": "shutdown",
"command_description": "Shut down authoritative DNS server",
"command_args": []
+ },
+ {
+ "command_name": "sendstats",
+ "command_description": "Send data to a statistics module at once",
+ "command_args": []
}
]
}
Modified: branches/trac448/src/bin/auth/auth_srv.cc
==============================================================================
--- branches/trac448/src/bin/auth/auth_srv.cc (original)
+++ branches/trac448/src/bin/auth/auth_srv.cc Mon Jan 3 19:27:41 2011
@@ -55,6 +55,7 @@
#include <auth/config.h>
#include <auth/auth_srv.h>
#include <auth/query.h>
+#include <auth/statistics.h>
using namespace std;
@@ -100,6 +101,9 @@
/// Hot spot cache
isc::datasrc::HotCache cache_;
+
+ /// Query counters for statistics
+ AuthCounters counters_;
private:
std::string db_file_;
@@ -111,6 +115,9 @@
bool xfrout_connected_;
AbstractXfroutClient& xfrout_client_;
+
+ /// Increment query counter
+ void incCounter(const int protocol);
};
AuthSrvImpl::AuthSrvImpl(const bool use_cache,
@@ -118,6 +125,7 @@
config_session_(NULL), verbose_mode_(false),
xfrin_session_(NULL),
memory_datasrc_class_(RRClass::IN()),
+ counters_(verbose_mode_),
xfrout_connected_(false),
xfrout_client_(xfrout_client)
{
@@ -154,33 +162,20 @@
AuthSrv* server_;
};
-// This is a derived class of \c DNSAnswer, to serve as a
-// callback in the asiolink module. It takes a completed
-// set of answer data from the DNS lookup and assembles it
-// into a wire-format response.
+// This is a derived class of \c DNSAnswer, to serve as a callback in the
+// asiolink module. We actually shouldn't do anything in this class because
+// we build complete response messages in the process methods; otherwise
+// the response message will contain trailing garbage. In future, we should
+// probably even drop the reliance on DNSAnswer. We don't need the coroutine
+// tricks provided in that framework, and its overhead would be significant
+// in terms of performance consideration for the authoritative server
+// implementation.
class MessageAnswer : public DNSAnswer {
public:
- MessageAnswer(AuthSrv* srv) : server_(srv) {}
- virtual void operator()(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer) const
- {
- MessageRenderer renderer(*buffer);
- if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
- ConstEDNSPtr edns(message->getEDNS());
- renderer.setLengthLimit(edns ? edns->getUDPSize() :
- Message::DEFAULT_MAX_UDPSIZE);
- } else {
- renderer.setLengthLimit(65535);
- }
- message->toWire(renderer);
- if (server_->getVerbose()) {
- cerr << "[b10-auth] sending a response (" << renderer.getLength()
- << " bytes):\n" << message->toText() << endl;
- }
- }
-
-private:
- AuthSrv* server_;
+ MessageAnswer(AuthSrv*) {}
+ virtual void operator()(const IOMessage&, MessagePtr,
+ OutputBufferPtr) const
+ {}
};
// This is a derived class of \c SimpleCallback, to serve
@@ -292,6 +287,11 @@
void
AuthSrv::setConfigSession(ModuleCCSession* config_session) {
impl_->config_session_ = config_session;
+}
+
+void
+AuthSrv::setStatisticsSession(AbstractSession* statistics_session) {
+ impl_->counters_.setStatisticsSession(statistics_session);
}
ModuleCCSession*
@@ -429,6 +429,9 @@
message->setHeaderFlag(Message::HEADERFLAG_AA);
message->setRcode(Rcode::NOERROR());
+ // Increment query counter.
+ incCounter(io_message.getSocket().getProtocol());
+
if (remote_edns) {
EDNSPtr local_edns = EDNSPtr(new EDNS());
local_edns->setDNSSECAwareness(dnssec_ok);
@@ -476,6 +479,9 @@
AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, MessagePtr message,
OutputBufferPtr buffer)
{
+ // Increment query counter.
+ incCounter(io_message.getSocket().getProtocol());
+
if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
if (verbose_mode_) {
cerr << "[b10-auth] AXFR query over UDP isn't allowed" << endl;
@@ -601,6 +607,19 @@
return (true);
}
+void
+AuthSrvImpl::incCounter(const int protocol) {
+ // Increment query counter.
+ if (protocol == IPPROTO_UDP) {
+ counters_.inc(AuthCounters::COUNTER_UDP_QUERY);
+ } else if (protocol == IPPROTO_TCP) {
+ counters_.inc(AuthCounters::COUNTER_TCP_QUERY);
+ } else {
+ // unknown protocol
+ isc_throw(Unexpected, "Unknown protocol: " << protocol);
+ }
+}
+
ConstElementPtr
AuthSrvImpl::setDbFile(ConstElementPtr config) {
ConstElementPtr answer = isc::config::createAnswer();
@@ -670,3 +689,12 @@
return (isc::config::createAnswer(1, error.what()));
}
}
+
+bool AuthSrv::submitStatistics() const {
+ return (impl_->counters_.submitStatistics());
+}
+
+uint64_t
+AuthSrv::getCounter(const AuthCounters::CounterType type) const {
+ return (impl_->counters_.getCounter(type));
+}
Modified: branches/trac448/src/bin/auth/auth_srv.h
==============================================================================
--- branches/trac448/src/bin/auth/auth_srv.h (original)
+++ branches/trac448/src/bin/auth/auth_srv.h Mon Jan 3 19:27:41 2011
@@ -27,6 +27,7 @@
#include <config/ccsession.h>
#include <asiolink/asiolink.h>
+#include <auth/statistics.h>
namespace isc {
namespace datasrc {
@@ -62,6 +63,7 @@
///
/// The design of this class is still in flux. It's quite likely to change
/// in future versions.
+///
class AuthSrv {
///
/// \name Constructors, Assignment Operator and Destructor.
@@ -96,6 +98,8 @@
/// \param message Pointer to the \c Message object
/// \param buffer Pointer to an \c OutputBuffer for the resposne
/// \param server Pointer to the \c DNSServer
+ ///
+ /// \throw isc::Unexpected Protocol type of \a message is unexpected
void processMessage(const asiolink::IOMessage& io_message,
isc::dns::MessagePtr message,
isc::dns::OutputBufferPtr buffer,
@@ -281,6 +285,49 @@
void setMemoryDataSrc(const isc::dns::RRClass& rrclass,
MemoryDataSrcPtr memory_datasrc);
+ /// \brief Set the communication session with Statistics.
+ ///
+ /// This function never throws an exception as far as
+ /// AuthCounters::setStatisticsSession() doesn't throw.
+ ///
+ /// Note: this interface is tentative. We'll revisit the ASIO and
+ /// session frameworks, at which point the session will probably
+ /// be passed on construction of the server.
+ ///
+ /// \param statistics_session A Session object over which statistics
+ /// information is exchanged with statistics module.
+ /// The session must be established before setting in the server
+ /// object.
+ /// Ownership isn't transferred: the caller is responsible for keeping
+ /// this object to be valid while the server object is working and for
+ /// disconnecting the session and destroying the object when the server
+ /// is shutdown.
+ void setStatisticsSession(isc::cc::AbstractSession* statistics_session);
+
+ /// \brief Submit statistics counters to statistics module.
+ ///
+ /// This function can throw an exception from
+ /// AuthCounters::submitStatistics().
+ ///
+ /// \return true on success, false on failure (e.g. session timeout,
+ /// session error).
+ bool submitStatistics() const;
+
+ /// \brief Get the value of counter in the AuthCounters.
+ ///
+ /// This function calls AuthCounters::getCounter() and
+ /// returns its return value.
+ ///
+ /// This function never throws an exception as far as
+ /// AuthCounters::getCounter() doesn't throw.
+ ///
+ /// Note: Currently this function is for testing purpose only.
+ ///
+ /// \param type Type of a counter to get the value of
+ ///
+ /// \return the value of the counter.
+ uint64_t getCounter(const AuthCounters::CounterType type) const;
+
private:
AuthSrvImpl* impl_;
asiolink::IOService* io_service_;
Modified: branches/trac448/src/bin/auth/benchmarks/Makefile.am
==============================================================================
--- branches/trac448/src/bin/auth/benchmarks/Makefile.am (original)
+++ branches/trac448/src/bin/auth/benchmarks/Makefile.am Mon Jan 3 19:27:41 2011
@@ -11,6 +11,7 @@
query_bench_SOURCES += ../query.h ../query.cc
query_bench_SOURCES += ../auth_srv.h ../auth_srv.cc
query_bench_SOURCES += ../config.h ../config.cc
+query_bench_SOURCES += ../statistics.h ../statistics.cc
query_bench_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
query_bench_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
Modified: branches/trac448/src/bin/auth/benchmarks/query_bench.cc
==============================================================================
--- branches/trac448/src/bin/auth/benchmarks/query_bench.cc (original)
+++ branches/trac448/src/bin/auth/benchmarks/query_bench.cc Mon Jan 3 19:27:41 2011
@@ -33,6 +33,7 @@
#include <xfr/xfrout_client.h>
#include <auth/auth_srv.h>
+#include <auth/config.h>
#include <auth/query.h>
#include <asiolink/asiolink.h>
@@ -53,24 +54,25 @@
// Just something to pass as the server to resume
class DummyServer : public DNSServer {
public:
- virtual void operator()(asio::error_code, size_t) { }
- virtual void resume(const bool) { }
+ virtual void operator()(asio::error_code, size_t) {}
+ virtual void resume(const bool) {}
virtual DNSServer* clone() {
- return new DummyServer(*this);
+ return (new DummyServer(*this));
}
};
class QueryBenchMark {
-private:
+protected:
// Maintain dynamically generated objects via shared pointers because
// QueryBenchMark objects will be copied.
typedef boost::shared_ptr<AuthSrv> AuthSrvPtr;
+private:
typedef boost::shared_ptr<const IOEndpoint> IOEndpointPtr;
-public:
- QueryBenchMark(const int cache_slots, const char* const datasrc_file,
+protected:
+ QueryBenchMark(const bool enable_cache,
const BenchQueries& queries, MessagePtr query_message,
OutputBufferPtr buffer) :
- server_(new AuthSrv(cache_slots >= 0 ? true : false, xfrout_client)),
+ server_(new AuthSrv(enable_cache, xfrout_client)),
queries_(queries),
query_message_(query_message),
buffer_(buffer),
@@ -78,13 +80,8 @@
dummy_endpoint(IOEndpointPtr(IOEndpoint::create(IPPROTO_UDP,
IOAddress("192.0.2.1"),
5300)))
- {
- if (cache_slots >= 0) {
- server_->setCacheSlots(cache_slots);
- }
- server_->updateConfig(Element::fromJSON("{\"database_file\": \"" +
- string(datasrc_file) + "\"}"));
- }
+ {}
+public:
unsigned int run() {
BenchQueries::const_iterator query;
const BenchQueries::const_iterator query_end = queries_.end();
@@ -93,14 +90,16 @@
IOMessage io_message(&(*query)[0], (*query).size(), dummy_socket,
*dummy_endpoint);
query_message_->clear(Message::PARSE);
+ buffer_->clear();
server_->processMessage(io_message, query_message_, buffer_,
- &server);
+ &server);
}
return (queries_.size());
}
+protected:
+ AuthSrvPtr server_;
private:
- AuthSrvPtr server_;
const BenchQueries& queries_;
MessagePtr query_message_;
OutputBufferPtr buffer_;
@@ -108,26 +107,92 @@
IOEndpointPtr dummy_endpoint;
};
+class Sqlite3QueryBenchMark : public QueryBenchMark {
+public:
+ Sqlite3QueryBenchMark(const int cache_slots,
+ const char* const datasrc_file,
+ const BenchQueries& queries,
+ MessagePtr query_message,
+ OutputBufferPtr buffer) :
+ QueryBenchMark(cache_slots >= 0 ? true : false, queries,
+ query_message, buffer)
+ {
+ if (cache_slots >= 0) {
+ server_->setCacheSlots(cache_slots);
+ }
+ server_->updateConfig(Element::fromJSON("{\"database_file\": \"" +
+ string(datasrc_file) + "\"}"));
+ }
+};
+
+class MemoryQueryBenchMark : public QueryBenchMark {
+public:
+ MemoryQueryBenchMark(const char* const zone_file,
+ const char* const zone_origin,
+ const BenchQueries& queries,
+ MessagePtr query_message,
+ OutputBufferPtr buffer) :
+ QueryBenchMark(false, queries, query_message, buffer)
+ {
+ configureAuthServer(*server_,
+ Element::fromJSON(
+ "{\"datasources\": "
+ " [{\"type\": \"memory\","
+ " \"zones\": [{\"origin\": \"" +
+ string(zone_origin) + "\","
+ " \"file\": \"" +
+ string(zone_file) + "\"}]}]}"));
+ }
+};
+
+void
+printQPSResult(unsigned int iteration, double duration,
+ double iteration_per_second)
+{
+ cout.precision(6);
+ cout << "Processed " << iteration << " queries in "
+ << fixed << duration << "s";
+ cout.precision(2);
+ cout << " (" << fixed << iteration_per_second << "qps)" << endl;
+}
}
namespace isc {
namespace bench {
template<>
void
-BenchMark<QueryBenchMark>::printResult() const {
- cout.precision(6);
- cout << "Processed " << getIteration() << " queries in "
- << fixed << getDuration() << "s";
- cout.precision(2);
- cout << " (" << fixed << getIterationPerSecond() << "qps)" << endl;
+BenchMark<Sqlite3QueryBenchMark>::printResult() const {
+ printQPSResult(getIteration(), getDuration(), getIterationPerSecond());
+}
+
+template<>
+void
+BenchMark<MemoryQueryBenchMark>::printResult() const {
+ printQPSResult(getIteration(), getDuration(), getIterationPerSecond());
}
}
}
namespace {
+const int ITERATION_DEFAULT = 1;
+enum DataSrcType {
+ SQLITE3,
+ MEMORY
+};
+
void
usage() {
- cerr << "Usage: query_bench [-n iterations] datasrc_file query_datafile"
+ cerr <<
+ "Usage: query_bench [-n iterations] [-t datasrc_type] [-o origin] "
+ "datasrc_file query_datafile\n"
+ " -n Number of iterations per test case (default: "
+ << ITERATION_DEFAULT << ")\n"
+ " -t Type of data source: sqlite3|memory (default: sqlite3)\n"
+ " -o Origin name of datasrc_file necessary for \"memory\", "
+ "ignored for others\n"
+ " datasrc_file: sqlite3 DB file for \"sqlite3\", "
+ "textual master file for \"memory\" datasrc\n"
+ " query_datafile: queryperf style input data"
<< endl;
exit (1);
}
@@ -136,12 +201,20 @@
int
main(int argc, char* argv[]) {
int ch;
- int iteration = 1;
- while ((ch = getopt(argc, argv, "n:")) != -1) {
+ int iteration = ITERATION_DEFAULT;
+ const char* opt_datasrc_type = "sqlite3";
+ const char* origin = NULL;
+ while ((ch = getopt(argc, argv, "n:t:o:")) != -1) {
switch (ch) {
case 'n':
iteration = atoi(optarg);
break;
+ case 't':
+ opt_datasrc_type = optarg;
+ break;
+ case 'o':
+ origin = optarg;
+ break;
case '?':
default:
usage();
@@ -155,6 +228,21 @@
const char* const datasrc_file = argv[0];
const char* const query_data_file = argv[1];
+ DataSrcType datasrc_type = SQLITE3;
+ if (strcmp(opt_datasrc_type, "sqlite3") == 0) {
+ ; // no need to override
+ } else if (strcmp(opt_datasrc_type, "memory") == 0) {
+ datasrc_type = MEMORY;
+ } else {
+ cerr << "Unknown data source type: " << datasrc_type << endl;
+ return (1);
+ }
+
+ if (datasrc_type == MEMORY && origin == NULL) {
+ cerr << "'-o Origin' is missing for memory data source " << endl;
+ return (1);
+ }
+
BenchQueries queries;
loadQueryData(query_data_file, queries, RRClass::IN());
OutputBufferPtr buffer(new OutputBuffer(4096));
@@ -162,32 +250,46 @@
cout << "Parameters:" << endl;
cout << " Iterations: " << iteration << endl;
- cout << " Data Source: " << datasrc_file << endl;
+ cout << " Data Source: type=" << opt_datasrc_type << ", file=" <<
+ datasrc_file << endl;
+ if (origin != NULL) {
+ cout << " Origin: " << origin << endl;
+ }
cout << " Query data: file=" << query_data_file << " (" << queries.size()
<< " queries)" << endl << endl;
- cout << "Benchmark enabling Hot Spot Cache with unlimited slots "
- << endl;
- BenchMark<QueryBenchMark>(iteration,
- QueryBenchMark(0, datasrc_file, queries, message,
- buffer));
-
- cout << "Benchmark enabling Hot Spot Cache with 10*#queries slots "
- << endl;
- BenchMark<QueryBenchMark>(iteration,
- QueryBenchMark(10 * queries.size(), datasrc_file,
+ switch (datasrc_type) {
+ case SQLITE3:
+ cout << "Benchmark enabling Hot Spot Cache with unlimited slots "
+ << endl;
+ BenchMark<Sqlite3QueryBenchMark>(
+ iteration, Sqlite3QueryBenchMark(0, datasrc_file, queries,
+ message, buffer));
+
+ cout << "Benchmark enabling Hot Spot Cache with 10*#queries slots "
+ << endl;
+ BenchMark<Sqlite3QueryBenchMark>(
+ iteration, Sqlite3QueryBenchMark(10 * queries.size(), datasrc_file,
queries, message, buffer));
- cout << "Benchmark enabling Hot Spot Cache with #queries/2 slots "
- << endl;
- BenchMark<QueryBenchMark>(iteration,
- QueryBenchMark(queries.size() / 2, datasrc_file,
+ cout << "Benchmark enabling Hot Spot Cache with #queries/2 slots "
+ << endl;
+ BenchMark<Sqlite3QueryBenchMark>(
+ iteration, Sqlite3QueryBenchMark(queries.size() / 2, datasrc_file,
queries, message, buffer));
- cout << "Benchmark disabling Hot Spot Cache" << endl;
- BenchMark<QueryBenchMark>(iteration,
- QueryBenchMark(-1, datasrc_file, queries,
- message, buffer));
+ cout << "Benchmark disabling Hot Spot Cache" << endl;
+ BenchMark<Sqlite3QueryBenchMark>(
+ iteration, Sqlite3QueryBenchMark(-1, datasrc_file, queries,
+ message, buffer));
+ break;
+ case MEMORY:
+ cout << "Benchmark with In Memory Data Source" << endl;
+ BenchMark<MemoryQueryBenchMark>(
+ iteration, MemoryQueryBenchMark(datasrc_file, origin, queries,
+ message, buffer));
+ break;
+ }
return (0);
}
Modified: branches/trac448/src/bin/auth/config.cc
==============================================================================
--- branches/trac448/src/bin/auth/config.cc (original)
+++ branches/trac448/src/bin/auth/config.cc Mon Jan 3 19:27:41 2011
@@ -151,16 +151,21 @@
isc_throw(AuthConfigError, "Missing zone file for zone: "
<< origin->str());
}
- const result::Result result = memory_datasrc_->addZone(
- ZonePtr(new MemoryZone(rrclass_, Name(origin->stringValue()))));
+ shared_ptr<MemoryZone> new_zone(new MemoryZone(rrclass_,
+ Name(origin->stringValue())));
+ const result::Result result = memory_datasrc_->addZone(new_zone);
if (result == result::EXIST) {
isc_throw(AuthConfigError, "zone "<< origin->str()
<< " already exists");
}
- // TODO
- // then load the zone from 'file', which is currently not implemented.
- //
+ /*
+ * TODO: Once we have better reloading of configuration (something
+ * else than throwing everything away and loading it again), we will
+ * need the load method to be split into some kind of build and
+ * commit/abort parts.
+ */
+ new_zone->load(file->stringValue());
}
}
Modified: branches/trac448/src/bin/auth/main.cc
==============================================================================
--- branches/trac448/src/bin/auth/main.cc (original)
+++ branches/trac448/src/bin/auth/main.cc Mon Jan 3 19:27:41 2011
@@ -25,7 +25,7 @@
#include <cassert>
#include <iostream>
-#include <boost/foreach.hpp>
+#include <boost/bind.hpp>
#include <exceptions/exceptions.h>
@@ -62,6 +62,10 @@
static const string PROGRAM = "Auth";
static const char* DNSPORT = "5300";
+// Note: this value must be greater than 0.
+// TODO: make it configurable via command channel.
+const uint32_t STATISTICS_SEND_INTERVAL_SEC = 60;
+
/* need global var for config/command handlers.
* todo: turn this around, and put handlers in the authserver
* class itself? */
@@ -84,6 +88,12 @@
answer = createAnswer(0, args);
} else if (command == "shutdown") {
io_service.stop();
+ } else if (command == "sendstats") {
+ if (verbose_mode) {
+ cerr << "[b10-auth] command 'sendstats' received" << endl;
+ }
+ assert(auth_server != NULL);
+ auth_server->submitStatistics();
}
return (answer);
@@ -103,6 +113,12 @@
cerr << "\t-v: verbose output" << endl;
exit(1);
}
+
+void
+statisticsTimerCallback(AuthSrv* auth_server) {
+ assert(auth_server != NULL);
+ auth_server->submitStatistics();
+}
} // end of anonymous namespace
int
@@ -168,7 +184,10 @@
// XXX: we should eventually pass io_service here.
Session* cc_session = NULL;
Session* xfrin_session = NULL;
+ Session* statistics_session = NULL;
+ IntervalTimer* itimer = NULL;
bool xfrin_session_established = false; // XXX (see Trac #287)
+ bool statistics_session_established = false; // XXX (see Trac #287)
ModuleCCSession* config_session = NULL;
string xfrout_socket_path;
if (getenv("B10_FROM_BUILD") != NULL) {
@@ -230,12 +249,19 @@
xfrin_session_established = true;
cout << "[b10-auth] Xfrin session channel established." << endl;
+ statistics_session = new Session(io_service.get_io_service());
+ cout << "[b10-auth] Statistics session channel created." << endl;
+ statistics_session->establish(NULL);
+ statistics_session_established = true;
+ cout << "[b10-auth] Statistics session channel established." << endl;
+
// XXX: with the current interface to asiolink we have to create
// auth_server before io_service while Session needs io_service.
// In a next step of refactoring we should make asiolink independent
// from auth_server, and create io_service, auth_server, and
// sessions in that order.
auth_server->setXfrinSession(xfrin_session);
+ auth_server->setStatisticsSession(statistics_session);
// Configure the server. configureAuthServer() is expected to install
// all initial configurations, but as a short term workaround we
@@ -245,6 +271,14 @@
configureAuthServer(*auth_server, config_session->getFullConfig());
auth_server->updateConfig(ElementPtr());
+ // create interval timer instance
+ itimer = new IntervalTimer(io_service);
+ // set up interval timer
+ // register function to send statistics with interval
+ itimer->setupTimer(boost::bind(statisticsTimerCallback, auth_server),
+ STATISTICS_SEND_INTERVAL_SEC);
+ cout << "[b10-auth] Interval timer to send statistics set." << endl;
+
cout << "[b10-auth] Server started." << endl;
io_service.run();
@@ -254,10 +288,16 @@
ret = 1;
}
+ if (statistics_session_established) {
+ statistics_session->disconnect();
+ }
+
if (xfrin_session_established) {
xfrin_session->disconnect();
}
+ delete itimer;
+ delete statistics_session;
delete xfrin_session;
delete config_session;
delete cc_session;
Modified: branches/trac448/src/bin/auth/query.cc
==============================================================================
--- branches/trac448/src/bin/auth/query.cc (original)
+++ branches/trac448/src/bin/auth/query.cc Mon Jan 3 19:27:41 2011
@@ -24,6 +24,24 @@
namespace isc {
namespace auth {
+
+void
+Query::putSOA(const Zone& zone) const {
+ Zone::FindResult soa_result(zone.find(zone.getOrigin(),
+ RRType::SOA()));
+ if (soa_result.code != Zone::SUCCESS) {
+ isc_throw(NoSOA, "There's no SOA record in zone " <<
+ zone.getOrigin().toText());
+ } else {
+ /*
+ * FIXME:
+ * The const-cast is wrong, but the Message interface seems
+ * to insist.
+ */
+ response_.addRRset(Message::SECTION_AUTHORITY,
+ boost::const_pointer_cast<RRset>(soa_result.rrset));
+ }
+}
void
Query::process() const {
@@ -59,12 +77,14 @@
// TODO : add NS to authority section, fill in additional section.
break;
case Zone::NXDOMAIN:
+ // Just empty answer with SOA in authority section
response_.setRcode(Rcode::NXDOMAIN());
- // TODO : add SOA to authority section
+ putSOA(*result.zone);
break;
case Zone::NXRRSET:
+ // Just empty answer with SOA in authority section
response_.setRcode(Rcode::NOERROR());
- // TODO : add SOA to authority section
+ putSOA(*result.zone);
break;
case Zone::CNAME:
case Zone::DNAME:
Modified: branches/trac448/src/bin/auth/query.h
==============================================================================
--- branches/trac448/src/bin/auth/query.h (original)
+++ branches/trac448/src/bin/auth/query.h Mon Jan 3 19:27:41 2011
@@ -14,6 +14,8 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
+#include <exceptions/exceptions.h>
+
namespace isc {
namespace dns {
class Message;
@@ -23,6 +25,7 @@
namespace datasrc {
class MemoryDataSrc;
+class Zone;
}
namespace auth {
@@ -100,15 +103,46 @@
/// providing compatible behavior may have its own benefit, so this point
/// should be revisited later.
///
- /// Right now this method never throws an exception, but it may in a
- /// future version.
+ /// This might throw BadZone or any of its specific subclasses, but that
+ /// shouldn't happen in real-life (as BadZone means wrong data, it should
+ /// have been rejected upon loading).
void process() const;
+
+ /// \short Bad zone data encountered.
+ ///
+ /// This is thrown when process encounteres misconfigured zone in a way
+ /// it can't continue. This throws, not sets the Rcode, because such
+ /// misconfigured zone should not be present in the data source and
+ /// should have been rejected sooner.
+ struct BadZone : public isc::Exception {
+ BadZone(const char* file, size_t line, const char* what) :
+ Exception(file, line, what)
+ {}
+ };
+
+ /// \short Zone is missing its SOA record.
+ ///
+ /// We tried to add a SOA into the authoritative section, but the zone
+ /// does not contain one.
+ struct NoSOA : public BadZone {
+ NoSOA(const char* file, size_t line, const char* what) :
+ BadZone(file, line, what)
+ {}
+ };
private:
const isc::datasrc::MemoryDataSrc& memory_datasrc_;
const isc::dns::Name& qname_;
const isc::dns::RRType& qtype_;
isc::dns::Message& response_;
+
+ /**
+ * \short Adds a SOA.
+ *
+ * Adds a SOA of the zone into the authority zone of response_.
+ * Can throw NoSOA.
+ */
+ void putSOA(const isc::datasrc::Zone& zone) const;
};
}
Modified: branches/trac448/src/bin/auth/tests/Makefile.am
==============================================================================
--- branches/trac448/src/bin/auth/tests/Makefile.am (original)
+++ branches/trac448/src/bin/auth/tests/Makefile.am Mon Jan 3 19:27:41 2011
@@ -22,10 +22,12 @@
run_unittests_SOURCES += ../query.h ../query.cc
run_unittests_SOURCES += ../change_user.h ../change_user.cc
run_unittests_SOURCES += ../config.h ../config.cc
+run_unittests_SOURCES += ../statistics.h ../statistics.cc
run_unittests_SOURCES += auth_srv_unittest.cc
run_unittests_SOURCES += config_unittest.cc
run_unittests_SOURCES += query_unittest.cc
run_unittests_SOURCES += change_user_unittest.cc
+run_unittests_SOURCES += statistics_unittest.cc
run_unittests_SOURCES += run_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
Modified: branches/trac448/src/bin/auth/tests/auth_srv_unittest.cc
==============================================================================
--- branches/trac448/src/bin/auth/tests/auth_srv_unittest.cc (original)
+++ branches/trac448/src/bin/auth/tests/auth_srv_unittest.cc Mon Jan 3 19:27:41 2011
@@ -15,14 +15,30 @@
// $Id$
#include <config.h>
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <dns/message.h>
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+#include <dns/rdataclass.h>
+
#include <datasrc/memory_datasrc.h>
#include <auth/auth_srv.h>
+#include <auth/statistics.h>
#include <dns/tests/unittest_util.h>
#include <testutils/srv_test.h>
+using namespace std;
using namespace isc::cc;
using namespace isc::dns;
+using namespace isc::dns::rdata;
using namespace isc::data;
using namespace isc::xfr;
using namespace asiolink;
@@ -41,15 +57,113 @@
protected:
AuthSrvTest() : server(true, xfrout), rrclass(RRClass::IN()) {
server.setXfrinSession(¬ify_session);
+ server.setStatisticsSession(&statistics_session);
}
virtual void processMessage() {
server.processMessage(*io_message, parse_message, response_obuffer,
&dnsserv);
}
+ MockSession statistics_session;
MockXfroutClient xfrout;
AuthSrv server;
const RRClass rrclass;
+ vector<uint8_t> response_data;
};
+
+// A helper function that builds a response to version.bind/TXT/CH that
+// should be identical to the response from our builtin (static) data source
+// by default. The resulting wire-format data will be stored in 'data'.
+void
+createBuiltinVersionResponse(const qid_t qid, vector<uint8_t>& data) {
+ const Name version_name("version.bind");
+ Message message(Message::RENDER);
+
+ UnitTestUtil::createRequestMessage(message, Opcode::QUERY(),
+ qid, version_name,
+ RRClass::CH(), RRType::TXT());
+ message.setHeaderFlag(Message::HEADERFLAG_QR);
+ message.setHeaderFlag(Message::HEADERFLAG_AA);
+ RRsetPtr rrset_version = RRsetPtr(new RRset(version_name, RRClass::CH(),
+ RRType::TXT(), RRTTL(0)));
+ rrset_version->addRdata(generic::TXT(PACKAGE_STRING));
+ message.addRRset(Message::SECTION_ANSWER, rrset_version);
+
+ RRsetPtr rrset_version_ns = RRsetPtr(new RRset(version_name, RRClass::CH(),
+ RRType::NS(), RRTTL(0)));
+ rrset_version_ns->addRdata(generic::NS(version_name));
+ message.addRRset(Message::SECTION_AUTHORITY, rrset_version_ns);
+
+ OutputBuffer obuffer(0);
+ MessageRenderer renderer(obuffer);
+ message.toWire(renderer);
+
+ data.clear();
+ data.assign(static_cast<const uint8_t*>(renderer.getData()),
+ static_cast<const uint8_t*>(renderer.getData()) +
+ renderer.getLength());
+}
+
+// In the following tests we confirm the response data is rendered in
+// wire format in the expected way.
+
+// The most primitive check: checking the result of the processMessage()
+// method
+TEST_F(AuthSrvTest, builtInQuery) {
+ UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
+ default_qid, Name("version.bind"),
+ RRClass::CH(), RRType::TXT());
+ createRequestPacket(request_message, IPPROTO_UDP);
+ server.processMessage(*io_message, parse_message, response_obuffer,
+ &dnsserv);
+ createBuiltinVersionResponse(default_qid, response_data);
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+ response_obuffer->getData(),
+ response_obuffer->getLength(),
+ &response_data[0], response_data.size());
+}
+
+// Same test emulating the UDPServer class behavior (defined in libasiolink).
+// This is not a good test in that it assumes internal implementation details
+// of UDPServer, but we've encountered a regression due to the introduction
+// of that class, so we add a test for that case to prevent such a regression
+// in future.
+// Besides, the generalization of UDPServer is probably too much for the
+// authoritative only server in terms of performance, and it's quite likely
+// we need to drop it for the authoritative server implementation.
+// At that point we can drop this test, too.
+TEST_F(AuthSrvTest, builtInQueryViaDNSServer) {
+ UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
+ default_qid, Name("version.bind"),
+ RRClass::CH(), RRType::TXT());
+ createRequestPacket(request_message, IPPROTO_UDP);
+
+ (*server.getDNSLookupProvider())(*io_message, parse_message,
+ response_obuffer, &dnsserv);
+ (*server.getDNSAnswerProvider())(*io_message, parse_message,
+ response_obuffer);
+
+ createBuiltinVersionResponse(default_qid, response_data);
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+ response_obuffer->getData(),
+ response_obuffer->getLength(),
+ &response_data[0], response_data.size());
+}
+
+// Same type of test as builtInQueryViaDNSServer but for an error response.
+TEST_F(AuthSrvTest, iqueryViaDNSServer) {
+ createDataFromFile("iquery_fromWire.wire");
+ (*server.getDNSLookupProvider())(*io_message, parse_message,
+ response_obuffer, &dnsserv);
+ (*server.getDNSAnswerProvider())(*io_message, parse_message,
+ response_obuffer);
+
+ UnitTestUtil::readWireData("iquery_response_fromWire.wire",
+ response_data);
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+ response_obuffer->getData(),
+ response_obuffer->getLength(),
+ &response_data[0], response_data.size());
+}
// Unsupported requests. Should result in NOTIMP.
TEST_F(AuthSrvTest, unsupportedRequest) {
@@ -440,4 +554,90 @@
server.setCacheSlots(0);
EXPECT_EQ(00, server.getCacheSlots());
}
-}
+
+// Submit UDP normal query and check query counter
+TEST_F(AuthSrvTest, queryCounterUDPNormal) {
+ // The counter should be initialized to 0.
+ EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_UDP_QUERY));
+ // Create UDP message and process.
+ UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
+ default_qid, Name("example.com"),
+ RRClass::IN(), RRType::NS());
+ createRequestPacket(request_message, IPPROTO_UDP);
+ server.processMessage(*io_message, parse_message, response_obuffer,
+ &dnsserv);
+ // After processing UDP query, the counter should be 1.
+ EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_UDP_QUERY));
+}
+
+// Submit TCP normal query and check query counter
+TEST_F(AuthSrvTest, queryCounterTCPNormal) {
+ // The counter should be initialized to 0.
+ EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
+ // Create TCP message and process.
+ UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
+ default_qid, Name("example.com"),
+ RRClass::IN(), RRType::NS());
+ createRequestPacket(request_message, IPPROTO_TCP);
+ server.processMessage(*io_message, parse_message, response_obuffer,
+ &dnsserv);
+ // After processing TCP query, the counter should be 1.
+ EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
+}
+
+// Submit TCP AXFR query and check query counter
+TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
+ // The counter should be initialized to 0.
+ EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
+ UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
+ Name("example.com"), RRClass::IN(), RRType::AXFR());
+ createRequestPacket(request_message, IPPROTO_TCP);
+ // On success, the AXFR query has been passed to a separate process,
+ // so we shouldn't have to respond.
+ server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ // After processing TCP AXFR query, the counter should be 1.
+ EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
+}
+
+// class for queryCounterUnexpected test
+// getProtocol() returns IPPROTO_IP
+class DummyUnknownSocket : public IOSocket {
+public:
+ DummyUnknownSocket() {}
+ virtual int getNative() const { return (0); }
+ virtual int getProtocol() const { return (IPPROTO_IP); }
+};
+
+// function for queryCounterUnexpected test
+// returns a reference to a static object of DummyUnknownSocket
+IOSocket&
+getDummyUnknownSocket() {
+ static DummyUnknownSocket socket;
+ return (socket);
+}
+
+// Submit unexpected type of query and check it throws isc::Unexpected
+TEST_F(AuthSrvTest, queryCounterUnexpected) {
+ // This code isn't exception safe, but we'd rather keep the code
+ // simpler and more readable as this is only for tests and if it throws
+ // the program would immediately terminate anyway.
+
+ // Create UDP query packet.
+ UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
+ default_qid, Name("example.com"),
+ RRClass::IN(), RRType::NS());
+ createRequestPacket(request_message, IPPROTO_UDP);
+
+ // Modify the message.
+ delete io_message;
+ endpoint = IOEndpoint::create(IPPROTO_UDP,
+ IOAddress(DEFAULT_REMOTE_ADDRESS), 5300);
+ io_message = new IOMessage(request_renderer.getData(),
+ request_renderer.getLength(),
+ getDummyUnknownSocket(), *endpoint);
+
+ EXPECT_THROW(server.processMessage(*io_message, parse_message,
+ response_obuffer, &dnsserv),
+ isc::Unexpected);
+}
+}
Modified: branches/trac448/src/bin/auth/tests/config_unittest.cc
==============================================================================
--- branches/trac448/src/bin/auth/tests/config_unittest.cc (original)
+++ branches/trac448/src/bin/auth/tests/config_unittest.cc Mon Jan 3 19:27:41 2011
@@ -17,6 +17,7 @@
#include <exceptions/exceptions.h>
#include <dns/rrclass.h>
+#include <dns/masterload.h>
#include <cc/data.h>
@@ -143,33 +144,42 @@
}
TEST_F(MemoryDatasrcConfigTest, addOneZone) {
- parser->build(Element::fromJSON(
- "[{\"type\": \"memory\","
- " \"zones\": [{\"origin\": \"example.com\","
- " \"file\": \"example.zone\"}]}]"));
- parser->commit();
- EXPECT_EQ(1, server.getMemoryDataSrc(rrclass)->getZoneCount());
+ EXPECT_NO_THROW(parser->build(Element::fromJSON(
+ "[{\"type\": \"memory\","
+ " \"zones\": [{\"origin\": \"example.com\","
+ " \"file\": \"" TEST_DATA_DIR
+ "/example.zone\"}]}]")));
+ EXPECT_NO_THROW(parser->commit());
+ EXPECT_EQ(1, server.getMemoryDataSrc(rrclass)->getZoneCount());
+ // Check it actually loaded something
+ EXPECT_EQ(Zone::SUCCESS, server.getMemoryDataSrc(rrclass)->findZone(
+ Name("ns.example.com.")).zone->find(Name("ns.example.com."),
+ RRType::A()).code);
}
TEST_F(MemoryDatasrcConfigTest, addMultiZones) {
- parser->build(Element::fromJSON(
- "[{\"type\": \"memory\","
- " \"zones\": [{\"origin\": \"example.com\","
- " \"file\": \"example.zone\"},"
+ EXPECT_NO_THROW(parser->build(Element::fromJSON(
+ "[{\"type\": \"memory\","
+ " \"zones\": [{\"origin\": \"example.com\","
+ " \"file\": \"" TEST_DATA_DIR
+ "/example.zone\"},"
" {\"origin\": \"example.org\","
- " \"file\": \"example.org.zone\"},"
+ " \"file\": \"" TEST_DATA_DIR
+ "/example.org.zone\"},"
" {\"origin\": \"example.net\","
- " \"file\": \"example.net.zone\"}]}]"));
- parser->commit();
+ " \"file\": \"" TEST_DATA_DIR
+ "/example.net.zone\"}]}]")));
+ EXPECT_NO_THROW(parser->commit());
EXPECT_EQ(3, server.getMemoryDataSrc(rrclass)->getZoneCount());
}
TEST_F(MemoryDatasrcConfigTest, replace) {
- parser->build(Element::fromJSON(
- "[{\"type\": \"memory\","
- " \"zones\": [{\"origin\": \"example.com\","
- " \"file\": \"example.zone\"}]}]"));
- parser->commit();
+ EXPECT_NO_THROW(parser->build(Element::fromJSON(
+ "[{\"type\": \"memory\","
+ " \"zones\": [{\"origin\": \"example.com\","
+ " \"file\": \"" TEST_DATA_DIR
+ "/example.zone\"}]}]")));
+ EXPECT_NO_THROW(parser->commit());
EXPECT_EQ(1, server.getMemoryDataSrc(rrclass)->getZoneCount());
EXPECT_EQ(isc::datasrc::result::SUCCESS,
server.getMemoryDataSrc(rrclass)->findZone(
@@ -179,31 +189,69 @@
// should replace the old one.
delete parser;
parser = createAuthConfigParser(server, "datasources");
- parser->build(Element::fromJSON(
+ EXPECT_NO_THROW(parser->build(Element::fromJSON(
"[{\"type\": \"memory\","
" \"zones\": [{\"origin\": \"example.org\","
- " \"file\": \"example.org.zone\"},"
+ " \"file\": \"" TEST_DATA_DIR
+ "/example.org.zone\"},"
" {\"origin\": \"example.net\","
- " \"file\": \"example.net.zone\"}]}]"));
- parser->commit();
+ " \"file\": \"" TEST_DATA_DIR
+ "/example.net.zone\"}]}]")));
+ EXPECT_NO_THROW(parser->commit());
EXPECT_EQ(2, server.getMemoryDataSrc(rrclass)->getZoneCount());
EXPECT_EQ(isc::datasrc::result::NOTFOUND,
server.getMemoryDataSrc(rrclass)->findZone(
Name("example.com")).code);
}
+TEST_F(MemoryDatasrcConfigTest, exception) {
+ // Load a zone
+ EXPECT_NO_THROW(parser->build(Element::fromJSON(
+ "[{\"type\": \"memory\","
+ " \"zones\": [{\"origin\": \"example.com\","
+ " \"file\": \"" TEST_DATA_DIR
+ "/example.zone\"}]}]")));
+ EXPECT_NO_THROW(parser->commit());
+ EXPECT_EQ(1, server.getMemoryDataSrc(rrclass)->getZoneCount());
+ EXPECT_EQ(isc::datasrc::result::SUCCESS,
+ server.getMemoryDataSrc(rrclass)->findZone(
+ Name("example.com")).code);
+
+ // create a new parser, and try to load something. It will throw,
+ // the given master file should not exist
+ delete parser;
+ parser = createAuthConfigParser(server, "datasources");
+ EXPECT_THROW(parser->build(Element::fromJSON(
+ "[{\"type\": \"memory\","
+ " \"zones\": [{\"origin\": \"example.org\","
+ " \"file\": \"" TEST_DATA_DIR
+ "/example.org.zone\"},"
+ " {\"origin\": \"example.net\","
+ " \"file\": \"" TEST_DATA_DIR
+ "/nonexistent.zone\"}]}]")), isc::dns::MasterLoadError);
+ // As that one throwed exception, it is not expected from us to
+ // commit it
+
+ // The original should be untouched
+ EXPECT_EQ(1, server.getMemoryDataSrc(rrclass)->getZoneCount());
+ EXPECT_EQ(isc::datasrc::result::SUCCESS,
+ server.getMemoryDataSrc(rrclass)->findZone(
+ Name("example.com")).code);
+}
+
TEST_F(MemoryDatasrcConfigTest, remove) {
- parser->build(Element::fromJSON(
- "[{\"type\": \"memory\","
- " \"zones\": [{\"origin\": \"example.com\","
- " \"file\": \"example.zone\"}]}]"));
- parser->commit();
+ EXPECT_NO_THROW(parser->build(Element::fromJSON(
+ "[{\"type\": \"memory\","
+ " \"zones\": [{\"origin\": \"example.com\","
+ " \"file\": \"" TEST_DATA_DIR
+ "/example.zone\"}]}]")));
+ EXPECT_NO_THROW(parser->commit());
EXPECT_EQ(1, server.getMemoryDataSrc(rrclass)->getZoneCount());
delete parser;
parser = createAuthConfigParser(server, "datasources");
- parser->build(Element::fromJSON("[]"));
- parser->commit();
+ EXPECT_NO_THROW(parser->build(Element::fromJSON("[]")));
+ EXPECT_NO_THROW(parser->commit());
EXPECT_EQ(AuthSrv::MemoryDataSrcPtr(), server.getMemoryDataSrc(rrclass));
}
@@ -212,9 +260,11 @@
Element::fromJSON(
"[{\"type\": \"memory\","
" \"zones\": [{\"origin\": \"example.com\","
- " \"file\": \"example.zone\"},"
+ " \"file\": \"" TEST_DATA_DIR
+ "/example.zone\"},"
" {\"origin\": \"example.com\","
- " \"file\": \"example.com.zone\"}]}]")),
+ " \"file\": \"" TEST_DATA_DIR
+ "/example.com.zone\"}]}]")),
AuthConfigError);
}
Modified: branches/trac448/src/bin/auth/tests/query_unittest.cc
==============================================================================
--- branches/trac448/src/bin/auth/tests/query_unittest.cc (original)
+++ branches/trac448/src/bin/auth/tests/query_unittest.cc Mon Jan 3 19:27:41 2011
@@ -28,10 +28,14 @@
using namespace isc::datasrc;
using namespace isc::auth;
+namespace {
+
RRsetPtr a_rrset = RRsetPtr(new RRset(Name("www.example.com"),
RRClass::IN(), RRType::A(),
RRTTL(3600)));
-namespace {
+RRsetPtr soa_rrset = RRsetPtr(new RRset(Name("example.com"),
+ RRClass::IN(), RRType::SOA(),
+ RRTTL(3600)));
// This is a mock Zone class for testing.
// It is a derived class of Zone, and simply hardcode the results of find()
// return SUCCESS for "www.example.com",
@@ -41,16 +45,20 @@
// else return DNAME
class MockZone : public Zone {
public:
- MockZone() : origin_(Name("example.com"))
+ MockZone(bool has_SOA = true) :
+ origin_(Name("example.com")),
+ has_SOA_(has_SOA)
{}
virtual const isc::dns::Name& getOrigin() const;
virtual const isc::dns::RRClass& getClass() const;
FindResult find(const isc::dns::Name& name,
- const isc::dns::RRType& type) const;
+ const isc::dns::RRType& type,
+ const FindOptions options = FIND_DEFAULT) const;
private:
Name origin_;
+ bool has_SOA_;
};
const Name&
@@ -64,20 +72,24 @@
}
Zone::FindResult
-MockZone::find(const Name& name, const RRType&) const {
+MockZone::find(const Name& name, const RRType& type, const FindOptions) const {
// hardcode the find results
if (name == Name("www.example.com")) {
- return FindResult(SUCCESS, a_rrset);
+ return (FindResult(SUCCESS, a_rrset));
+ } else if (name == Name("example.com") && type == RRType::SOA() &&
+ has_SOA_)
+ {
+ return (FindResult(SUCCESS, soa_rrset));
} else if (name == Name("delegation.example.com")) {
- return FindResult(DELEGATION, RRsetPtr());
+ return (FindResult(DELEGATION, RRsetPtr()));
} else if (name == Name("nxdomain.example.com")) {
- return FindResult(NXDOMAIN, RRsetPtr());
+ return (FindResult(NXDOMAIN, RRsetPtr()));
} else if (name == Name("nxrrset.example.com")) {
- return FindResult(NXRRSET, RRsetPtr());
+ return (FindResult(NXRRSET, RRsetPtr()));
} else if (name == Name("cname.example.com")) {
- return FindResult(CNAME, RRsetPtr());
+ return (FindResult(CNAME, RRsetPtr()));
} else {
- return FindResult(DNAME, RRsetPtr());
+ return (FindResult(DNAME, RRsetPtr()));
}
}
@@ -106,7 +118,7 @@
}
TEST_F(QueryTest, matchZone) {
- // match qname, normal query
+ // add a matching zone.
memory_datasrc.addZone(ZonePtr(new MockZone()));
query.process();
EXPECT_EQ(Rcode::NOERROR(), response.getRcode());
@@ -119,12 +131,39 @@
Query nxdomain_query(memory_datasrc, nxdomain_name, qtype, response);
nxdomain_query.process();
EXPECT_EQ(Rcode::NXDOMAIN(), response.getRcode());
+ EXPECT_EQ(0, response.getRRCount(Message::SECTION_ANSWER));
+ EXPECT_EQ(0, response.getRRCount(Message::SECTION_ADDITIONAL));
+ EXPECT_TRUE(response.hasRRset(Message::SECTION_AUTHORITY,
+ Name("example.com"), RRClass::IN(), RRType::SOA()));
// NXRRSET
const Name nxrrset_name(Name("nxrrset.example.com"));
Query nxrrset_query(memory_datasrc, nxrrset_name, qtype, response);
nxrrset_query.process();
EXPECT_EQ(Rcode::NOERROR(), response.getRcode());
+ EXPECT_EQ(0, response.getRRCount(Message::SECTION_ANSWER));
+ EXPECT_EQ(0, response.getRRCount(Message::SECTION_ADDITIONAL));
+ EXPECT_TRUE(response.hasRRset(Message::SECTION_AUTHORITY,
+ Name("example.com"), RRClass::IN(), RRType::SOA()));
+}
+
+/*
+ * This tests that when there's no SOA and we need a negative answer. It should
+ * throw in that case.
+ */
+TEST_F(QueryTest, noSOA) {
+ memory_datasrc.addZone(ZonePtr(new MockZone(false)));
+
+ // The NX Domain
+ const Name nxdomain_name(Name("nxdomain.example.com"));
+ Query nxdomain_query(memory_datasrc, nxdomain_name, qtype, response);
+ EXPECT_THROW(nxdomain_query.process(), Query::NoSOA);
+ // Of course, we don't look into the response, as it throwed
+
+ // NXRRSET
+ const Name nxrrset_name(Name("nxrrset.example.com"));
+ Query nxrrset_query(memory_datasrc, nxrrset_name, qtype, response);
+ EXPECT_THROW(nxrrset_query.process(), Query::NoSOA);
}
TEST_F(QueryTest, noMatchZone) {
@@ -136,4 +175,5 @@
nomatch_query.process();
EXPECT_EQ(Rcode::REFUSED(), response.getRcode());
}
+
}
Modified: branches/trac448/src/bin/bind10/tests/Makefile.am
==============================================================================
--- branches/trac448/src/bin/bind10/tests/Makefile.am (original)
+++ branches/trac448/src/bin/bind10/tests/Makefile.am Mon Jan 3 19:27:41 2011
@@ -1,12 +1,17 @@
+PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
+#PYTESTS = args_test.py bind10_test.py
PYTESTS = bind10_test.py
EXTRA_DIST = $(PYTESTS)
-# 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
check-local:
+if ENABLE_PYTHON_COVERAGE
+ touch $(abs_top_srcdir)/.coverage
+ rm -f .coverage
+ ${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
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/bin/bind10 \
- $(PYCOVERAGE) $(abs_srcdir)/$$pytest || exit ; \
+ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
Modified: branches/trac448/src/bin/bindctl/tests/Makefile.am
==============================================================================
--- branches/trac448/src/bin/bindctl/tests/Makefile.am (original)
+++ branches/trac448/src/bin/bindctl/tests/Makefile.am Mon Jan 3 19:27:41 2011
@@ -1,12 +1,16 @@
+PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
PYTESTS = bindctl_test.py
EXTRA_DIST = $(PYTESTS)
-# 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
check-local:
+if ENABLE_PYTHON_COVERAGE
+ touch $(abs_top_srcdir)/.coverage
+ rm -f .coverage
+ ${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
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_srcdir)/src/bin \
- $(PYCOVERAGE) $(abs_srcdir)/$$pytest || exit ; \
+ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
Modified: branches/trac448/src/bin/cfgmgr/tests/Makefile.am
==============================================================================
--- branches/trac448/src/bin/cfgmgr/tests/Makefile.am (original)
+++ branches/trac448/src/bin/cfgmgr/tests/Makefile.am Mon Jan 3 19:27:41 2011
@@ -1,13 +1,17 @@
+PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
PYTESTS = b10-cfgmgr_test.py
EXTRA_DIST = $(PYTESTS)
-# 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
check-local:
+if ENABLE_PYTHON_COVERAGE
+ touch $(abs_top_srcdir)/.coverage
+ rm -f .coverage
+ ${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
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/bin/cfgmgr \
- $(PYCOVERAGE) $(abs_builddir)/$$pytest || exit ; \
+ $(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \
done
Modified: branches/trac448/src/bin/cmdctl/tests/Makefile.am
==============================================================================
--- branches/trac448/src/bin/cmdctl/tests/Makefile.am (original)
+++ branches/trac448/src/bin/cmdctl/tests/Makefile.am Mon Jan 3 19:27:41 2011
@@ -1,14 +1,18 @@
+PYCOVERAGE_RUN=@PYCOVERAGE_RUN@
PYTESTS = cmdctl_test.py
EXTRA_DIST = $(PYTESTS)
-# 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
check-local:
+if ENABLE_PYTHON_COVERAGE
+ touch $(abs_top_srcdir)/.coverage
+ rm -f .coverage
+ ${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
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/bin/cmdctl \
CMDCTL_SPEC_PATH=$(abs_top_builddir)/src/bin/cmdctl \
CMDCTL_SRC_PATH=$(abs_top_srcdir)/src/bin/cmdctl \
- $(PYCOVERAGE) $(abs_srcdir)/$$pytest || exit ; \
+ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
Modified: branches/trac448/src/bin/loadzone/tests/correct/Makefile.am
==============================================================================
--- branches/trac448/src/bin/loadzone/tests/correct/Makefile.am (original)
+++ branches/trac448/src/bin/loadzone/tests/correct/Makefile.am Mon Jan 3 19:27:41 2011
@@ -1,4 +1,3 @@
-PYTESTS = correct_test.sh
EXTRA_DIST = get_zonedatas.py
EXTRA_DIST += include.db
EXTRA_DIST += inclsub.db
@@ -14,12 +13,8 @@
EXTRA_DIST += ttlext.db
EXTRA_DIST += example.db
-# later will have configure option to choose this, like: coverage run --branch
-PYCOVERAGE = $(PYTHON)
+# TODO: maybe use TESTS?
# test using command-line arguments, so use check-local target instead of TESTS
check-local:
- 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/bin/loadzone \
- $(SHELL) $(abs_builddir)/$$pytest || exit ; \
- done
+ echo Running test: correct_test.sh
+ $(SHELL) $(abs_builddir)/correct_test.sh
Modified: branches/trac448/src/bin/loadzone/tests/error/Makefile.am
==============================================================================
--- branches/trac448/src/bin/loadzone/tests/error/Makefile.am (original)
+++ branches/trac448/src/bin/loadzone/tests/error/Makefile.am Mon Jan 3 19:27:41 2011
@@ -1,5 +1,3 @@
-PYTESTS = error_test.sh
-
EXTRA_DIST = error.known
EXTRA_DIST += formerr1.db
EXTRA_DIST += formerr2.db
@@ -14,12 +12,8 @@
EXTRA_DIST += originerr1.db
EXTRA_DIST += originerr2.db
-# later will have configure option to choose this, like: coverage run --branch
-PYCOVERAGE = $(PYTHON)
+# TODO: use TESTS ?
# test using command-line arguments, so use check-local target instead of TESTS
check-local:
- for pytest in $(PYTESTS) ; do \
- echo Running test: $$pytest ; \
- env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/bin/loadzone \
- $(SHELL) $(abs_builddir)/$$pytest || exit ; \
- done
+ echo Running test: error_test.sh
+ $(SHELL) $(abs_builddir)/error_test.sh
Modified: branches/trac448/src/bin/msgq/tests/Makefile.am
==============================================================================
--- branches/trac448/src/bin/msgq/tests/Makefile.am (original)
+++ branches/trac448/src/bin/msgq/tests/Makefile.am Mon Jan 3 19:27:41 2011
@@ -1,14 +1,18 @@
+PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
PYTESTS = msgq_test.py
EXTRA_DIST = $(PYTESTS)
-# 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
check-local:
+if ENABLE_PYTHON_COVERAGE
+ touch $(abs_top_srcdir)/.coverage
+ rm -f .coverage
+ ${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
env PYTHONPATH=$(abs_top_builddir)/src/bin/msgq:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \
BIND10_TEST_SOCKET_FILE=$(builddir)/test_msgq_socket.sock \
- $(PYCOVERAGE) $(abs_srcdir)/$$pytest || exit ; \
+ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
Modified: branches/trac448/src/bin/recurse/b10-recurse.8
==============================================================================
--- branches/trac448/src/bin/recurse/b10-recurse.8 (original)
+++ branches/trac448/src/bin/recurse/b10-recurse.8 Mon Jan 3 19:27:41 2011
@@ -2,12 +2,12 @@
.\" Title: b10-recurse
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: September 16, 2010
+.\" Date: December 27, 2010
.\" Manual: BIND10
.\" Source: BIND10
.\" Language: English
.\"
-.TH "B10\-RECURSE" "8" "September 16, 2010" "BIND10" "BIND10"
+.TH "B10\-RECURSE" "8" "December 27, 2010" "BIND10" "BIND10"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
@@ -22,7 +22,7 @@
b10-recurse \- Recursive DNS server
.SH "SYNOPSIS"
.HP \w'\fBb10\-recurse\fR\ 'u
-\fBb10\-recurse\fR [\fB\-4\fR] [\fB\-6\fR] [\fB\-a\ \fR\fB\fIaddress\fR\fR] [\fB\-n\fR] [\fB\-p\ \fR\fB\fInumber\fR\fR] [\fB\-u\ \fR\fB\fIusername\fR\fR] [\fB\-v\fR]
+\fBb10\-recurse\fR [\fB\-u\ \fR\fB\fIusername\fR\fR] [\fB\-v\fR]
.SH "DESCRIPTION"
.PP
The
@@ -60,55 +60,6 @@
.PP
The arguments are as follows:
.PP
-\fB\-4\fR
-.RS 4
-Enables IPv4 only mode\&. This switch may not be used with
-\fB\-6\fR
-nor
-\fB\-a\fR\&. By default, it listens on both IPv4 and IPv6 (if capable)\&.
-.RE
-.PP
-\fB\-6\fR
-.RS 4
-Enables IPv6 only mode\&. This switch may not be used with
-\fB\-4\fR
-nor
-\fB\-a\fR\&. By default, it listens on both IPv4 and IPv6 (if capable)\&.
-.RE
-.PP
-\fB\-a \fR\fB\fIaddress\fR\fR
-.RS 4
-The IPv4 or IPv6 address to listen on\&. This switch may not be used with
-\fB\-4\fR
-nor
-\fB\-6\fR\&. The default is to listen on all addresses\&. (This is a short term workaround\&. This argument may change\&.)
-.RE
-.PP
-\fB\-n\fR
-.RS 4
-Do not cache answers in memory\&. The default is to use the cache for faster responses\&. The cache keeps the most recent 30,000 answers (positive and negative) in memory for 30 seconds (instead of querying the data source, such as SQLite3 database, each time)\&.
-.RE
-.PP
-\fB\-p \fR\fB\fInumber\fR\fR
-.RS 4
-The port number it listens on\&. The default is 5300\&.
-.if n \{\
-.sp
-.\}
-.RS 4
-.it 1 an-trap
-.nr an-no-space-flag 1
-.nr an-break-flag 1
-.br
-.ps +1
-\fBNote\fR
-.ps -1
-.br
-The Y1 prototype runs on all interfaces and on this nonstandard port\&.
-.sp .5v
-.RE
-.RE
-.PP
\fB\-u \fR\fB\fIusername\fR\fR
.RS 4
The user name of the
@@ -130,7 +81,6 @@
\fBb10-cfgmgr\fR(8),
\fBb10-cmdctl\fR(8),
-\fBb10-loadzone\fR(8),
\fBb10-msgq\fR(8),
\fBbind10\fR(8),
BIND 10 Guide\&.
Modified: branches/trac448/src/bin/recurse/b10-recurse.xml
==============================================================================
--- branches/trac448/src/bin/recurse/b10-recurse.xml (original)
+++ branches/trac448/src/bin/recurse/b10-recurse.xml Mon Jan 3 19:27:41 2011
@@ -21,7 +21,7 @@
<refentry>
<refentryinfo>
- <date>September 16, 2010</date>
+ <date>December 27, 2010</date>
</refentryinfo>
<refmeta>
@@ -45,11 +45,6 @@
<refsynopsisdiv>
<cmdsynopsis>
<command>b10-recurse</command>
- <arg><option>-4</option></arg>
- <arg><option>-6</option></arg>
- <arg><option>-a <replaceable>address</replaceable></option></arg>
- <arg><option>-n</option></arg>
- <arg><option>-p <replaceable>number</replaceable></option></arg>
<arg><option>-u <replaceable>username</replaceable></option></arg>
<arg><option>-v</option></arg>
</cmdsynopsis>
@@ -89,59 +84,6 @@
<para>The arguments are as follows:</para>
<variablelist>
- <varlistentry>
- <term><option>-4</option></term>
- <listitem><para>
- Enables IPv4 only mode.
- This switch may not be used with <option>-6</option> nor
- <option>-a</option>.
- By default, it listens on both IPv4 and IPv6 (if capable).
- </para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term><option>-6</option></term>
- <listitem><para>
- Enables IPv6 only mode.
- This switch may not be used with <option>-4</option> nor
- <option>-a</option>.
- By default, it listens on both IPv4 and IPv6 (if capable).
- </para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term><option>-a <replaceable>address</replaceable></option></term>
-
- <listitem>
- <para>The IPv4 or IPv6 address to listen on.
- This switch may not be used with <option>-4</option> nor
- <option>-6</option>.
- The default is to listen on all addresses.
- (This is a short term workaround. This argument may change.)
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><option>-n</option></term>
- <listitem><para>
- Do not cache answers in memory.
- The default is to use the cache for faster responses.
- The cache keeps the most recent 30,000 answers (positive
- and negative) in memory for 30 seconds (instead of querying
- the data source, such as SQLite3 database, each time).
- </para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term><option>-p <replaceable>number</replaceable></option></term>
- <listitem><para>
- The port number it listens on.
- The default is 5300.</para>
- <note><simpara>The Y1 prototype runs on all interfaces
- and on this nonstandard port.</simpara></note>
- </listitem>
- </varlistentry>
<varlistentry>
<term><option>-u <replaceable>username</replaceable></option></term>
@@ -187,9 +129,6 @@
<refentrytitle>b10-cmdctl</refentrytitle><manvolnum>8</manvolnum>
</citerefentry>,
<citerefentry>
- <refentrytitle>b10-loadzone</refentrytitle><manvolnum>8</manvolnum>
- </citerefentry>,
- <citerefentry>
<refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum>
</citerefentry>,
<citerefentry>
Modified: branches/trac448/src/bin/stats/tests/Makefile.am
==============================================================================
--- branches/trac448/src/bin/stats/tests/Makefile.am (original)
+++ branches/trac448/src/bin/stats/tests/Makefile.am Mon Jan 3 19:27:41 2011
@@ -1,15 +1,19 @@
SUBDIRS = isc testdata
+PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
PYTESTS = b10-stats_test.py b10-stats_stub_test.py
EXTRA_DIST = $(PYTESTS) fake_time.py
CLEANFILES = fake_time.pyc
-# 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
check-local:
+if ENABLE_PYTHON_COVERAGE
+ touch $(abs_top_srcdir)/.coverage
+ rm -f .coverage
+ ${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
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/bin/stats:$(abs_top_builddir)/src/bin/stats/tests \
B10_FROM_BUILD=$(abs_top_builddir) \
- $(PYCOVERAGE) $(abs_srcdir)/$$pytest || exit ; \
+ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
Modified: branches/trac448/src/bin/tests/Makefile.am
==============================================================================
--- branches/trac448/src/bin/tests/Makefile.am (original)
+++ branches/trac448/src/bin/tests/Makefile.am Mon Jan 3 19:27:41 2011
@@ -1,13 +1,17 @@
+PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
PYTESTS = process_rename_test.py
# .py will be generated by configure, so we don't have to include it
# in EXTRA_DIST.
-# 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
check-local:
+if ENABLE_PYTHON_COVERAGE
+ touch $(abs_top_srcdir)/.coverage
+ rm -f .coverage
+ ${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
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 \
- $(PYCOVERAGE) $(abs_builddir)/$$pytest || exit ; \
+ $(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \
done
Modified: branches/trac448/src/bin/xfrin/tests/Makefile.am
==============================================================================
--- branches/trac448/src/bin/xfrin/tests/Makefile.am (original)
+++ branches/trac448/src/bin/xfrin/tests/Makefile.am Mon Jan 3 19:27:41 2011
@@ -1,3 +1,4 @@
+PYCOVERAGE_RUN=@PYCOVERAGE_RUN@
PYTESTS = xfrin_test.py
EXTRA_DIST = $(PYTESTS)
@@ -8,13 +9,16 @@
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)
# test using command-line arguments, so use check-local target instead of TESTS
check-local:
+if ENABLE_PYTHON_COVERAGE
+ touch $(abs_top_srcdir)/.coverage
+ rm -f .coverage
+ ${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
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 || exit ; \
+ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
Modified: branches/trac448/src/bin/xfrout/tests/Makefile.am
==============================================================================
--- branches/trac448/src/bin/xfrout/tests/Makefile.am (original)
+++ branches/trac448/src/bin/xfrout/tests/Makefile.am Mon Jan 3 19:27:41 2011
@@ -1,3 +1,4 @@
+PYCOVERAGE_RUN=@PYCOVERAGE_RUN@
PYTESTS = xfrout_test.py
EXTRA_DIST = $(PYTESTS)
@@ -8,13 +9,16 @@
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)
# test using command-line arguments, so use check-local target instead of TESTS
check-local:
+if ENABLE_PYTHON_COVERAGE
+ touch $(abs_top_srcdir)/.coverage
+ rm -f .coverage
+ ${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
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 || exit ; \
+ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
Modified: branches/trac448/src/bin/zonemgr/tests/Makefile.am
==============================================================================
--- branches/trac448/src/bin/zonemgr/tests/Makefile.am (original)
+++ branches/trac448/src/bin/zonemgr/tests/Makefile.am Mon Jan 3 19:27:41 2011
@@ -1,14 +1,17 @@
+PYCOVERAGE_RUN=@PYCOVERAGE_RUN@
PYTESTS = zonemgr_test.py
EXTRA_DIST = $(PYTESTS)
-
CLEANFILES = initdb.file
-# 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
check-local:
+if ENABLE_PYTHON_COVERAGE
+ touch $(abs_top_srcdir)/.coverage
+ rm -f .coverage
+ ${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
env PYTHONPATH=$(abs_top_builddir)/src/bin/zonemgr:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/xfr/.libs \
- $(PYCOVERAGE) $(abs_srcdir)/$$pytest || exit ; \
+ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
Modified: branches/trac448/src/lib/asiolink/asiolink.cc
==============================================================================
--- branches/trac448/src/lib/asiolink/asiolink.cc (original)
+++ branches/trac448/src/lib/asiolink/asiolink.cc Mon Jan 3 19:27:41 2011
@@ -26,6 +26,7 @@
#include <asio.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/bind.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/shared_ptr.hpp>
@@ -374,4 +375,90 @@
timeout_, retries_);
}
-}
+class IntervalTimerImpl {
+private:
+ // prohibit copy
+ IntervalTimerImpl(const IntervalTimerImpl& source);
+ IntervalTimerImpl& operator=(const IntervalTimerImpl& source);
+public:
+ IntervalTimerImpl(IOService& io_service);
+ ~IntervalTimerImpl();
+ void setupTimer(const IntervalTimer::Callback& cbfunc,
+ const uint32_t interval);
+ void callback(const asio::error_code& error);
+private:
+ // a function to update timer_ when it expires
+ void updateTimer();
+ // a function to call back when timer_ expires
+ IntervalTimer::Callback cbfunc_;
+ // interval in seconds
+ uint32_t interval_;
+ // asio timer
+ asio::deadline_timer timer_;
+};
+
+IntervalTimerImpl::IntervalTimerImpl(IOService& io_service) :
+ timer_(io_service.get_io_service())
+{}
+
+IntervalTimerImpl::~IntervalTimerImpl()
+{}
+
+void
+IntervalTimerImpl::setupTimer(const IntervalTimer::Callback& cbfunc,
+ const uint32_t interval)
+{
+ // Interval should not be 0.
+ if (interval == 0) {
+ isc_throw(isc::BadValue, "Interval should not be 0");
+ }
+ // Call back function should not be empty.
+ if (cbfunc.empty()) {
+ isc_throw(isc::InvalidParameter, "Callback function is empty");
+ }
+ cbfunc_ = cbfunc;
+ interval_ = interval;
+ // Set initial expire time.
+ // At this point the timer is not running yet and will not expire.
+ // After calling IOService::run(), the timer will expire.
+ updateTimer();
+ return;
+}
+
+void
+IntervalTimerImpl::updateTimer() {
+ try {
+ // Update expire time to (current time + interval_).
+ timer_.expires_from_now(boost::posix_time::seconds(interval_));
+ } catch (const asio::system_error& e) {
+ isc_throw(isc::Unexpected, "Failed to update timer");
+ }
+ // Reset timer.
+ timer_.async_wait(boost::bind(&IntervalTimerImpl::callback, this, _1));
+}
+
+void
+IntervalTimerImpl::callback(const asio::error_code& cancelled) {
+ // Do not call cbfunc_ in case the timer was cancelled.
+ // The timer will be canelled in the destructor of asio::deadline_timer.
+ if (!cancelled) {
+ cbfunc_();
+ // Set next expire time.
+ updateTimer();
+ }
+}
+
+IntervalTimer::IntervalTimer(IOService& io_service) {
+ impl_ = new IntervalTimerImpl(io_service);
+}
+
+IntervalTimer::~IntervalTimer() {
+ delete impl_;
+}
+
+void
+IntervalTimer::setupTimer(const Callback& cbfunc, const uint32_t interval) {
+ return (impl_->setupTimer(cbfunc, interval));
+}
+
+}
Modified: branches/trac448/src/lib/asiolink/asiolink.h
==============================================================================
--- branches/trac448/src/lib/asiolink/asiolink.h (original)
+++ branches/trac448/src/lib/asiolink/asiolink.h Mon Jan 3 19:27:41 2011
@@ -23,6 +23,7 @@
#include <unistd.h> // for some network system calls
#include <asio/ip/address.hpp>
#include <boost/shared_ptr.hpp>
+#include <boost/function.hpp>
#include <functional>
#include <string>
@@ -98,6 +99,7 @@
namespace asiolink {
class DNSServiceImpl;
struct IOServiceImpl;
+struct IntervalTimerImpl;
/// \brief An exception that is thrown if an error occurs within the IO
/// module. This is mainly intended to be a wrapper exception class for
@@ -233,7 +235,7 @@
/// that share the same \c io_service with the authoritative server.
/// It will eventually be removed once the wrapper interface is
/// generalized.
- asio::io_service& get_io_service() { return io_service_.get_io_service(); };
+ asio::io_service& get_io_service() { return io_service_.get_io_service(); }
private:
DNSServiceImpl* impl_;
IOService& io_service_;
@@ -567,6 +569,98 @@
unsigned retries_;
};
+/// \brief The \c IntervalTimer class is a wrapper for the ASIO
+/// \c asio::deadline_timer class.
+///
+/// This class is implemented to use \c asio::deadline_timer as
+/// interval timer.
+///
+/// \c setupTimer() sets a timer to expire on (now + interval) and
+/// a call back function.
+///
+/// \c IntervalTimerImpl::callback() is called by the timer when
+/// it expires.
+///
+/// The function calls the call back function set by \c setupTimer()
+/// and updates the timer to expire in (now + interval) seconds.
+/// The type of call back function is \c void(void).
+///
+/// The call back function will not be called if the instance of this
+/// class is destructed before the timer is expired.
+///
+/// Note: Destruction of an instance of this class while call back
+/// is pending causes throwing an exception from \c IOService.
+///
+/// Sample code:
+/// \code
+/// void function_to_call_back() {
+/// // this function will be called periodically
+/// }
+/// int interval_in_seconds = 1;
+/// IOService io_service;
+///
+/// IntervalTimer intervalTimer(io_service);
+/// intervalTimer.setupTimer(function_to_call_back, interval_in_seconds);
+/// io_service.run();
+/// \endcode
+///
+class IntervalTimer {
+public:
+ /// \name The type of timer callback function
+ typedef boost::function<void()> Callback;
+
+ ///
+ /// \name Constructors and Destructor
+ ///
+ /// Note: The copy constructor and the assignment operator are
+ /// intentionally defined as private, making this class non-copyable.
+ //@{
+private:
+ IntervalTimer(const IntervalTimer& source);
+ IntervalTimer& operator=(const IntervalTimer& source);
+public:
+ /// \brief The constructor with \c IOService.
+ ///
+ /// This constructor may throw a standard exception if
+ /// memory allocation fails inside the method.
+ /// This constructor may also throw \c asio::system_error.
+ ///
+ /// \param io_service A reference to an instance of IOService
+ ///
+ IntervalTimer(IOService& io_service);
+
+ /// \brief The destructor.
+ ///
+ /// This destructor never throws an exception.
+ ///
+ /// On the destruction of this class the timer will be canceled
+ /// inside \c asio::deadline_timer.
+ ///
+ ~IntervalTimer();
+ //@}
+
+ /// \brief Register timer callback function and interval.
+ ///
+ /// This function sets callback function and interval in seconds.
+ /// Timer will actually start after calling \c IOService::run().
+ ///
+ /// \param cbfunc A reference to a function \c void(void) to call back
+ /// when the timer is expired (should not be an empty functor)
+ /// \param interval Interval in seconds (greater than 0)
+ ///
+ /// Note: IntervalTimer will not pass \c asio::error_code to
+ /// call back function. In case the timer is cancelled, the function
+ /// will not be called.
+ ///
+ /// \throw isc::InvalidParameter cbfunc is empty
+ /// \throw isc::BadValue interval is 0
+ /// \throw isc::Unexpected ASIO library error
+ ///
+ void setupTimer(const Callback& cbfunc, const uint32_t interval);
+private:
+ IntervalTimerImpl* impl_;
+};
+
} // asiolink
#endif // __ASIOLINK_H
Modified: branches/trac448/src/lib/asiolink/tests/asiolink_unittest.cc
==============================================================================
--- branches/trac448/src/lib/asiolink/tests/asiolink_unittest.cc (original)
+++ branches/trac448/src/lib/asiolink/tests/asiolink_unittest.cc Mon Jan 3 19:27:41 2011
@@ -24,6 +24,7 @@
#include <boost/lexical_cast.hpp>
#include <boost/bind.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <gtest/gtest.h>
@@ -59,6 +60,9 @@
// two octets encode the length of the rest of the data. This is crucial
// for the tests below.
const uint8_t test_data[] = {0, 4, 1, 2, 3, 4};
+// TODO: Consider this margin
+const boost::posix_time::time_duration TIMER_MARGIN_MSEC =
+ boost::posix_time::milliseconds(50);
TEST(IOAddressTest, fromText) {
IOAddress io_address_v4("192.0.2.1");
@@ -742,4 +746,247 @@
EXPECT_EQ(3, num);
}
-}
+// This fixture is for testing IntervalTimer. Some callback functors are
+// registered as callback function of the timer to test if they are called
+// or not.
+class IntervalTimerTest : public ::testing::Test {
+protected:
+ IntervalTimerTest() : io_service_() {};
+ ~IntervalTimerTest() {}
+ class TimerCallBack : public std::unary_function<void, void> {
+ public:
+ TimerCallBack(IntervalTimerTest* test_obj) : test_obj_(test_obj) {}
+ void operator()() const {
+ test_obj_->timer_called_ = true;
+ test_obj_->io_service_.stop();
+ return;
+ }
+ private:
+ IntervalTimerTest* test_obj_;
+ };
+ class TimerCallBackCounter : public std::unary_function<void, void> {
+ public:
+ TimerCallBackCounter(IntervalTimerTest* test_obj) : test_obj_(test_obj) {
+ counter_ = 0;
+ }
+ void operator()() {
+ ++counter_;
+ return;
+ }
+ int counter_;
+ private:
+ IntervalTimerTest* test_obj_;
+ };
+ class TimerCallBackCancelDeleter : public std::unary_function<void, void> {
+ public:
+ TimerCallBackCancelDeleter(IntervalTimerTest* test_obj,
+ IntervalTimer* timer,
+ TimerCallBackCounter& counter)
+ : test_obj_(test_obj), timer_(timer), counter_(counter), count_(0)
+ {}
+ void operator()() {
+ ++count_;
+ if (count_ == 1) {
+ // First time of call back.
+ // Store the value of counter_.counter_.
+ prev_counter_ = counter_.counter_;
+ delete timer_;
+ } else if (count_ == 2) {
+ // Second time of call back.
+ // Stop io_service to stop all timers.
+ test_obj_->io_service_.stop();
+ // Compare the value of counter_.counter_ with stored one.
+ // If TimerCallBackCounter was not called (expected behavior),
+ // they are same.
+ if (counter_.counter_ == prev_counter_) {
+ test_obj_->timer_cancel_success_ = true;
+ }
+ }
+ return;
+ }
+ private:
+ IntervalTimerTest* test_obj_;
+ IntervalTimer* timer_;
+ TimerCallBackCounter& counter_;
+ int count_;
+ int prev_counter_;
+ };
+ class TimerCallBackOverwriter : public std::unary_function<void, void> {
+ public:
+ TimerCallBackOverwriter(IntervalTimerTest* test_obj,
+ IntervalTimer& timer)
+ : test_obj_(test_obj), timer_(timer), count_(0)
+ {}
+ void operator()() {
+ ++count_;
+ if (count_ == 1) {
+ // First time of call back.
+ // Call setupTimer() to update callback function
+ // to TimerCallBack.
+ test_obj_->timer_called_ = false;
+ timer_.setupTimer(TimerCallBack(test_obj_), 1);
+ } else if (count_ == 2) {
+ // Second time of call back.
+ // If it reaches here, re-setupTimer() is failed (unexpected).
+ // We should stop here.
+ test_obj_->io_service_.stop();
+ }
+ return;
+ }
+ private:
+ IntervalTimerTest* test_obj_;
+ IntervalTimer& timer_;
+ int count_;
+ };
+protected:
+ IOService io_service_;
+ bool timer_called_;
+ bool timer_cancel_success_;
+};
+
+TEST_F(IntervalTimerTest, invalidArgumentToIntervalTimer) {
+ // Create asio_link::IntervalTimer and setup.
+ IntervalTimer itimer(io_service_);
+ // expect throw if call back function is empty
+ EXPECT_THROW(itimer.setupTimer(IntervalTimer::Callback(), 1),
+ isc::InvalidParameter);
+ // expect throw if interval is 0
+ EXPECT_THROW(itimer.setupTimer(TimerCallBack(this), 0), isc::BadValue);
+}
+
+TEST_F(IntervalTimerTest, startIntervalTimer) {
+ // Create asio_link::IntervalTimer and setup.
+ // Then run IOService and test if the callback function is called.
+ IntervalTimer itimer(io_service_);
+ timer_called_ = false;
+ // store start time
+ boost::posix_time::ptime start;
+ start = boost::posix_time::microsec_clock::universal_time();
+ // setup timer
+ itimer.setupTimer(TimerCallBack(this), 1);
+ io_service_.run();
+ // reaches here after timer expired
+ // delta: difference between elapsed time and 1 second
+ boost::posix_time::time_duration delta =
+ (boost::posix_time::microsec_clock::universal_time() - start)
+ - boost::posix_time::seconds(1);
+ if (delta.is_negative()) {
+ delta.invert_sign();
+ }
+ // expect TimerCallBack is called; timer_called_ is true
+ EXPECT_TRUE(timer_called_);
+ // expect interval is 1 second +/- TIMER_MARGIN_MSEC.
+ EXPECT_TRUE(delta < TIMER_MARGIN_MSEC);
+}
+
+TEST_F(IntervalTimerTest, destructIntervalTimer) {
+ // Note: This test currently takes 6 seconds. The timer should have
+ // finer granularity and timer periods in this test should be shorter
+ // in the future.
+ // This code isn't exception safe, but we'd rather keep the code
+ // simpler and more readable as this is only for tests and if it throws
+ // the program would immediately terminate anyway.
+
+ // The call back function will not be called after the timer is
+ // destructed.
+ //
+ // There are two timers:
+ // itimer_counter (A)
+ // (Calls TimerCallBackCounter)
+ // - increments internal counter in callback function
+ // itimer_canceller (B)
+ // (Calls TimerCallBackCancelDeleter)
+ // - first time of callback, it stores the counter value of
+ // callback_canceller and destructs itimer_counter
+ // - second time of callback, it compares the counter value of
+ // callback_canceller with stored value
+ // if they are same the timer was not called; expected result
+ // if they are different the timer was called after destructed
+ //
+ // 0 1 2 3 4 5 6 (s)
+ // (A) i-----+--x
+ // ^
+ // |destruct itimer_counter
+ // (B) i--------+--------s
+ // ^stop io_service
+ // and test itimer_counter have been stopped
+ //
+
+ // itimer_counter will be deleted in TimerCallBackCancelDeleter
+ IntervalTimer* itimer_counter = new IntervalTimer(io_service_);
+ IntervalTimer itimer_canceller(io_service_);
+ timer_cancel_success_ = false;
+ TimerCallBackCounter callback_canceller(this);
+ itimer_counter->setupTimer(callback_canceller, 2);
+ itimer_canceller.setupTimer(
+ TimerCallBackCancelDeleter(this, itimer_counter,
+ callback_canceller),
+ 3);
+ io_service_.run();
+ EXPECT_TRUE(timer_cancel_success_);
+}
+
+TEST_F(IntervalTimerTest, overwriteIntervalTimer) {
+ // Note: This test currently takes 4 seconds. The timer should have
+ // finer granularity and timer periods in this test should be shorter
+ // in the future.
+
+ // Calling setupTimer() multiple times updates call back function
+ // and interval.
+ //
+ // There are two timers:
+ // itimer (A)
+ // (Calls TimerCallBackCounter / TimerCallBack)
+ // - increments internal counter in callback function
+ // (TimerCallBackCounter)
+ // interval: 2 seconds
+ // - io_service_.stop() (TimerCallBack)
+ // interval: 1 second
+ // itimer_overwriter (B)
+ // (Calls TimerCallBackOverwriter)
+ // - first time of callback, it calls setupTimer() to change
+ // call back function and interval of itimer to
+ // TimerCallBack / 1 second
+ // after 3 + 1 seconds from the beginning of this test,
+ // TimerCallBack() will be called and io_service_ stops.
+ // - second time of callback, it means the test fails.
+ //
+ // 0 1 2 3 4 5 6 (s)
+ // (A) i-----+--C--s
+ // ^ ^stop io_service
+ // |change call back function
+ // (B) i--------+--------S
+ // ^(stop io_service on fail)
+ //
+
+ IntervalTimer itimer(io_service_);
+ IntervalTimer itimer_overwriter(io_service_);
+ // store start time
+ boost::posix_time::ptime start;
+ start = boost::posix_time::microsec_clock::universal_time();
+ itimer.setupTimer(TimerCallBackCounter(this), 2);
+ itimer_overwriter.setupTimer(TimerCallBackOverwriter(this, itimer), 3);
+ io_service_.run();
+ // reaches here after timer expired
+ // if interval is updated, it takes
+ // 3 seconds for TimerCallBackOverwriter
+ // + 1 second for TimerCallBack (stop)
+ // = 4 seconds.
+ // otherwise (test fails), it takes
+ // 3 seconds for TimerCallBackOverwriter
+ // + 3 seconds for TimerCallBackOverwriter (stop)
+ // = 6 seconds.
+ // delta: difference between elapsed time and 3 + 1 seconds
+ boost::posix_time::time_duration delta =
+ (boost::posix_time::microsec_clock::universal_time() - start)
+ - boost::posix_time::seconds(3 + 1);
+ if (delta.is_negative()) {
+ delta.invert_sign();
+ }
+ // expect callback function is updated: TimerCallBack is called
+ EXPECT_TRUE(timer_called_);
+ // expect interval is updated
+ EXPECT_TRUE(delta < TIMER_MARGIN_MSEC);
+}
+
+}
Modified: branches/trac448/src/lib/datasrc/memory_datasrc.cc
==============================================================================
--- branches/trac448/src/lib/datasrc/memory_datasrc.cc (original)
+++ branches/trac448/src/lib/datasrc/memory_datasrc.cc Mon Jan 3 19:27:41 2011
@@ -13,10 +13,13 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <map>
+#include <cassert>
#include <boost/shared_ptr.hpp>
+#include <boost/bind.hpp>
#include <dns/name.h>
#include <dns/rrclass.h>
+#include <dns/masterload.h>
#include <datasrc/memory_datasrc.h>
#include <datasrc/rbtree.h>
@@ -51,12 +54,186 @@
* that.
*/
typedef map<RRType, ConstRRsetPtr> Domain;
+ typedef Domain::value_type DomainPair;
typedef boost::shared_ptr<Domain> DomainPtr;
// The tree stores domains
typedef RBTree<Domain> DomainTree;
typedef RBNode<Domain> DomainNode;
// The actual zone data
DomainTree domains_;
+
+ /*
+ * Implementation of longer methods. We put them here, because the
+ * access is without the impl_-> and it will get inlined anyway.
+ */
+ // Implementation of MemoryZone::add
+ result::Result add(const ConstRRsetPtr& rrset, DomainTree* domains) {
+ // Sanitize input
+ if (!rrset) {
+ isc_throw(NullRRset, "The rrset provided is NULL");
+ }
+ Name name(rrset->getName());
+ NameComparisonResult compare(origin_.compare(name));
+ if (compare.getRelation() != NameComparisonResult::SUPERDOMAIN &&
+ compare.getRelation() != NameComparisonResult::EQUAL)
+ {
+ isc_throw(OutOfZone, "The name " << name <<
+ " is not contained in zone " << origin_);
+ }
+ // Get the node
+ DomainNode* node;
+ switch (domains->insert(name, &node)) {
+ // Just check it returns reasonable results
+ case DomainTree::SUCCEED:
+ case DomainTree::ALREADYEXIST:
+ break;
+ // Something odd got out
+ default:
+ assert(0);
+ }
+ assert(node);
+
+ // Now get the domain
+ DomainPtr domain;
+ // It didn't exist yet, create it
+ if (node->isEmpty()) {
+ domain.reset(new Domain);
+ node->setData(domain);
+ } else { // Get existing one
+ domain = node->getData();
+ }
+
+ // Try inserting the rrset there
+ if (domain->insert(DomainPair(rrset->getType(), rrset)).second) {
+ // Ok, we just put it in
+
+ // If this RRset creates a zone cut at this node, mark the node
+ // indicating the need for callback in find().
+ // TBD: handle DNAME, too
+ if (rrset->getType() == RRType::NS() &&
+ rrset->getName() != origin_) {
+ node->enableCallback();
+ }
+
+ return (result::SUCCESS);
+ } else {
+ // The RRSet of given type was already there
+ return (result::EXIST);
+ }
+ }
+
+ /*
+ * Same as above, but it checks the return value and if it already exists,
+ * it throws.
+ */
+ void addFromLoad(const ConstRRsetPtr& set, DomainTree* domains) {
+ switch (add(set, domains)) {
+ case result::EXIST:
+ isc_throw(dns::MasterLoadError, "Duplicate rrset: " <<
+ set->toText());
+ case result::SUCCESS:
+ return;
+ default:
+ assert(0);
+ }
+ }
+
+ // Maintain intermediate data specific to the search context used in
+ /// \c find().
+ ///
+ /// It will be passed to \c zonecutCallback() and record a possible
+ /// zone cut node and related RRset (normally NS or DNAME).
+ struct FindState {
+ FindState(FindOptions options) : zonecut_node_(NULL),
+ options_(options)
+ {}
+ const DomainNode* zonecut_node_;
+ ConstRRsetPtr rrset_;
+ const FindOptions options_;
+ };
+
+ // A callback called from possible zone cut nodes. This will be passed
+ // from the \c find() method to \c RBTree::find().
+ static bool zonecutCallback(const DomainNode& node, FindState* state) {
+ // We perform callback check only for the highest zone cut in the
+ // rare case of nested zone cuts.
+ if (state->zonecut_node_ != NULL) {
+ return (false);
+ }
+
+ const Domain::const_iterator found(node.getData()->find(RRType::NS()));
+ if (found != node.getData()->end()) {
+ // BIND 9 checks if this node is not the origin. But it cannot
+ // be the origin because we don't enable the callback at the
+ // origin node (see MemoryZoneImpl::add()). Or should we do a
+ // double check for it?
+ state->zonecut_node_ = &node;
+ state->rrset_ = found->second;
+
+ // Unless glue is allowed the search stops here, so we return
+ // false; otherwise return true to continue the search.
+ return ((state->options_ & FIND_GLUE_OK) == 0);
+ }
+
+ // This case should not happen because we enable callback only
+ // when we add an RR searched for above.
+ assert(0);
+ // This is here to avoid warning (therefore compilation error)
+ // in case assert is turned off. Otherwise we could get "Control
+ // reached end of non-void function".
+ return (false);
+ }
+
+ // Implementation of MemoryZone::find
+ FindResult find(const Name& name, RRType type,
+ const FindOptions options) const
+ {
+ // Get the node
+ DomainNode* node(NULL);
+ FindState state(options);
+ switch (domains_.find(name, &node, zonecutCallback, &state)) {
+ case DomainTree::PARTIALMATCH:
+ if (state.zonecut_node_ != NULL) {
+ return (FindResult(DELEGATION, state.rrset_));
+ }
+ // TODO: we should also cover empty non-terminal cases, which
+ // will require non trivial code and is deferred for later
+ // development. For now, we regard any partial match that
+ // didn't hit a zone cut as "not found".
+ case DomainTree::NOTFOUND:
+ return (FindResult(NXDOMAIN, ConstRRsetPtr()));
+ case DomainTree::EXACTMATCH: // This one is OK, handle it
+ break;
+ default:
+ assert(0);
+ }
+ assert(node);
+ assert(!node->isEmpty());
+
+ Domain::const_iterator found;
+
+ // If the node callback is enabled, this may be a zone cut. If it
+ // has a NS RR, we should return a delegation.
+ if (node->isCallbackEnabled()) {
+ found = node->getData()->find(RRType::NS());
+ if (found != node->getData()->end()) {
+ return (FindResult(DELEGATION, found->second));
+ }
+ }
+
+ found = node->getData()->find(type);
+ if (found != node->getData()->end()) {
+ // Good, it is here
+ return (FindResult(SUCCESS, found->second));
+ } else {
+ /*
+ * TODO Look for CNAME and DNAME (it should be OK to do so when
+ * the value is not found, as CNAME/DNAME domain should be
+ * empty otherwise.)
+ */
+ return (FindResult(NXRRSET, ConstRRsetPtr()));
+ }
+ }
};
MemoryZone::MemoryZone(const RRClass& zone_class, const Name& origin) :
@@ -79,9 +256,27 @@
}
Zone::FindResult
-MemoryZone::find(const Name&, const RRType&) const {
- // This is a tentative implementation that always returns NXDOMAIN.
- return (FindResult(NXDOMAIN, RRsetPtr()));
+MemoryZone::find(const Name& name, const RRType& type,
+ const FindOptions options) const
+{
+ return (impl_->find(name, type, options));
+}
+
+result::Result
+MemoryZone::add(const ConstRRsetPtr& rrset) {
+ return (impl_->add(rrset, &impl_->domains_));
+}
+
+
+void
+MemoryZone::load(const string& filename) {
+ // Load it into a temporary tree
+ MemoryZoneImpl::DomainTree tmp;
+ masterLoad(filename.c_str(), getOrigin(), getClass(),
+ boost::bind(&MemoryZoneImpl::addFromLoad, impl_, _1, &tmp));
+ // If it went well, put it inside
+ tmp.swap(impl_->domains_);
+ // And let the old data die with tmp
}
/// Implementation details for \c MemoryDataSrc hidden from the public
Modified: branches/trac448/src/lib/datasrc/memory_datasrc.h
==============================================================================
--- branches/trac448/src/lib/datasrc/memory_datasrc.h (original)
+++ branches/trac448/src/lib/datasrc/memory_datasrc.h Mon Jan 3 19:27:41 2011
@@ -58,9 +58,70 @@
/// \brief Looks up an RRset in the zone.
///
/// See documentation in \c Zone.
+ ///
+ /// It returns NULL pointer in case of NXDOMAIN and NXRRSET
+ /// (the base class documentation does not seem to require that).
virtual FindResult find(const isc::dns::Name& name,
- const isc::dns::RRType& type) const;
-
+ const isc::dns::RRType& type,
+ const FindOptions options = FIND_DEFAULT) const;
+
+ /// \brief Inserts an rrset into the zone.
+ ///
+ /// It puts another RRset into the zone.
+ ///
+ /// It throws NullRRset or OutOfZone if the provided rrset is invalid. It
+ /// might throw standard allocation exceptions, in which case this function
+ /// does not guarantee strong exception safety (it is currently not needed,
+ /// if it is needed in future, it should be implemented).
+ ///
+ /// \param rrset The set to add.
+ /// \return SUCCESS or EXIST (if an rrset for given name and type already
+ /// exists).
+ result::Result add(const isc::dns::ConstRRsetPtr& rrset);
+
+ /// \brief RRSet out of zone exception.
+ ///
+ /// This is thrown if addition of an RRset that doesn't belong under the
+ /// zone's origin is requested.
+ struct OutOfZone : public InvalidParameter {
+ OutOfZone(const char* file, size_t line, const char* what) :
+ InvalidParameter(file, line, what)
+ { }
+ };
+
+ /// \brief RRset is NULL exception.
+ ///
+ /// This is thrown if the provided RRset parameter is NULL.
+ struct NullRRset : public InvalidParameter {
+ NullRRset(const char* file, size_t line, const char* what) :
+ InvalidParameter(file, line, what)
+ { }
+ };
+
+ /// \brief Load zone from masterfile.
+ ///
+ /// This loads data from masterfile specified by filename. It replaces
+ /// current content. The masterfile parsing ability is kind of limited,
+ /// see isc::dns::masterLoad.
+ ///
+ /// This throws isc::dns::MasterLoadError if there is problem with loading
+ /// (missing file, malformed, it contains different zone, etc - see
+ /// isc::dns::masterLoad for details).
+ ///
+ /// In case of internal problems, OutOfZone, NullRRset or AssertError could
+ /// be thrown, but they should not be expected. Exceptions caused by
+ /// allocation may be thrown as well.
+ ///
+ /// If anything is thrown, the previous content is preserved (so it can
+ /// be used to update the data, but if user makes a typo, the old one
+ /// is kept).
+ ///
+ /// \param filename The master file to load.
+ ///
+ /// \todo We may need to split it to some kind of build and commit/abort.
+ /// This will probably be needed when a better implementation of
+ /// configuration reloading is written.
+ void load(const std::string& filename);
private:
/// \name Hidden private data
//@{
Modified: branches/trac448/src/lib/datasrc/rbtree.h
==============================================================================
--- branches/trac448/src/lib/datasrc/rbtree.h (original)
+++ branches/trac448/src/lib/datasrc/rbtree.h Mon Jan 3 19:27:41 2011
@@ -38,8 +38,11 @@
/// Helper function to remove the base domain from super domain
///
/// the precondition of this function is the super_name contains the
-/// sub_name so \code Name a("a.b.c"); Name b("b.c");
-/// Name c = a - b; \\c will be "a" \endcode
+/// sub_name so
+/// \code Name a("a.b.c");
+/// Name b("b.c");
+/// Name c = a - b;
+/// \endcode
///
/// \note function in this namespace is not intended to be used outside.
inline isc::dns::Name
@@ -51,8 +54,9 @@
template <typename T>
class RBTree;
+
/// \brief \c RBNode use by RBTree to store any data related to one domain name
-
+///
/// It has two roles, the first one is as one node in the \c RBTree,
/// the second one is to store the data related to one domain name and maintain
/// the domain name hierarchy struct in one domain name space.
@@ -74,11 +78,11 @@
friend class RBTree<T>;
typedef boost::shared_ptr<T> NodeDataPtr;
- /// \name Deonstructor
+ /// \name Destructor
/// \note it's seems a little strange that constructor is private
/// but deconstructor left public, the reason is for some smart pointer
/// like std::auto_ptr, they needs to delete RBNode in sometimes, but
- /// \code delete *pointer_to_node \codeend shouldn't be called directly
+ /// \code delete *pointer_to_node \endcode shouldn't be called directly
//@{
~RBNode();
//@}
@@ -111,6 +115,24 @@
void setData(const NodeDataPtr& data) { data_ = data; }
//@}
+ /// \name Callback related methods
+ ///
+ /// See the description of \c RBTree<T>::find() about callbacks.
+ ///
+ /// These methods never throw an exception.
+ //@{
+ /// Return if callback is enabled at the node.
+ ///
+ /// This method never throws an exception.
+ bool isCallbackEnabled() const { return (callback_required_); }
+
+ /// Enable callback at the node.
+ void enableCallback() { callback_required_ = true; }
+
+ /// Disable callback at the node.
+ void disableCallback() { callback_required_ = false; }
+ //@}
+
private:
/// \brief Define rbnode color
@@ -147,6 +169,7 @@
isc::dns::Name name_;
NodeDataPtr data_;
+
/// the down pointer points to the root node of sub domains of current
/// domain
/// \par Adding down pointer to \c RBNode is for two purpose:
@@ -154,6 +177,10 @@
/// big flat tree into several hierarchy trees
/// \li It save memory useage, so same label won't be saved several times
RBNode<T>* down_;
+
+ // If true, callback should be called at this node in search.
+ // (This may have to become part of more general "attribute flags")
+ bool callback_required_;
};
@@ -167,7 +194,8 @@
color_(BLACK),
// dummy name, the value doesn't matter:
name_(isc::dns::Name::ROOT_NAME()),
- down_(this)
+ down_(this),
+ callback_required_(false)
{
}
@@ -178,7 +206,8 @@
right_(NULL_NODE()),
color_(RED),
name_(name),
- down_(NULL_NODE())
+ down_(NULL_NODE()),
+ callback_required_(false)
{
}
@@ -186,51 +215,55 @@
template <typename T>
RBNode<T>::~RBNode() {
}
-/// \brief \c RBTree class represents all the domains with the same suffix,
-/// so it can be used to store the domains in one zone.
-///
-/// \c RBTree is a generic red black tree, and contains all the nodes with
-/// the same suffix, since each name may have sub domain names
-/// so \c RBTree is a recursive data structure namely tree in tree.
-/// So for one zone, several RBTrees may be involved. But from outside, the sub
-/// tree is opaque for end users.
-///
-/// \c RBTree split the domain space into hierarchy red black trees, nodes in one
-/// tree has the same base name. The benefit of this struct is that:
-/// - enhance the query performace compared with one big flat red black tree
-/// - decrase the memory footprint to save common labels only once.
-
-/*
-/// \verbatim
-/// with the following names:
-/// a x.d.e.f o.w.y.d.e.f
-/// b z.d.e.f p.w.y.d.e.f
-/// c g.h q.w.y.d.e.f
-/// the tree will looks like:
-/// b
-/// / \
-/// a d.e.f
-/// /|\
-/// c | g.h
-/// |
-/// w.y
-/// /|\
-/// x | z
-/// |
-/// p
-/// / \
-/// o q
-/// \endverbatim
-/// \note open problems:
-/// - current find funciton only return non-empty nodes, so there is no difference
-/// between find one not exist name with empty non-terminal nodes, but in DNS query
-/// logic, they are different
-/// \todo
-/// - add remove interface
-/// - add iterator to iterate the whole rbtree while may needed by axfr
-/// - since \c RBNode only has down pointer without up pointer, the node path during finding
-/// should be recorded for later use
-*/
+
+// note: the following class description is documented using C-style comments
+// because the verbatim diagram contain a backslash, which could be interpreted
+// as part of a multi-line comment with C++ style comments.
+/**
+ * \brief \c RBTree class represents all the domains with the same suffix,
+ * so it can be used to store the domains in one zone.
+ *
+ * \c RBTree is a generic red black tree, and contains all the nodes with
+ * the same suffix, since each name may have sub domain names
+ * so \c RBTree is a recursive data structure namely tree in tree.
+ * So for one zone, several RBTrees may be involved. But from outside, the sub
+ * tree is opaque for end users.
+ *
+ * \c RBTree split the domain space into hierarchy red black trees, nodes in one
+ * tree has the same base name. The benefit of this struct is that:
+ * - enhance the query performace compared with one big flat red black tree
+ * - decrase the memory footprint to save common labels only once.
+ *
+ * \verbatim
+ with the following names:
+ a x.d.e.f o.w.y.d.e.f
+ b z.d.e.f p.w.y.d.e.f
+ c g.h q.w.y.d.e.f
+ the tree will looks like:
+ b
+ / \
+ a d.e.f
+ /|\
+ c | g.h
+ |
+ w.y
+ /|\
+ x | z
+ |
+ p
+ / \
+ o q
+ * \endverbatim
+ * \note open problems:
+ * - current find funciton only return non-empty nodes, so there is no difference
+ * between find one not exist name with empty non-terminal nodes, but in DNS query
+ * logic, they are different
+ * \todo
+ * - add remove interface
+ * - add iterator to iterate the whole rbtree while may needed by axfr
+ * - since \c RBNode only has down pointer without up pointer, the node path during finding
+ * should be recorded for later use
+ */
template <typename T>
class RBTree : public boost::noncopyable {
friend class RBNode<T>;
@@ -247,7 +280,7 @@
/// \name Constructor and Destructor
//@{
- RBTree();
+ explicit RBTree();
/// \b Note: RBTree is not intended to be inherited so the destructor
/// is not virtual
@@ -256,13 +289,98 @@
/// \name Inquery methods
//@{
- /// \brief Find the node with the name
+ /// \brief Find the node that gives a longest match against the given name
+ ///
+ /// This method searches the \c RBTree for a node whose name is a longest
+ /// match against \c name. The found node, if any, is returned via the
+ /// \c node pointer.
+ /// By default, nodes that don't have data will be ignored, and the result
+ /// can be \c NOTFOUND even if there is a node whose name matches the
+ /// given \c name.
+ /// We'll soon introduce a "no data OK" mode in this method. It would
+ /// match any node of the tree regardless of whether the node has data
+ /// or not.
+ /// Since the tree is "compressed", i.e., a node can contain multiple
+ /// name labels, there are counter intuitive cases in the "no data OK"
+ /// mode. For example, see the diagram of the class description.
+ /// Name "y.d.e.f" is logically contained in the tree as part of the
+ /// "compressed" node of "w.y". But the search logic of this method
+ /// cannot find the logical match, and would return a \c PARTIALMATCH
+ /// result pointing to node "d.e.f". To correctly identify the real
+ /// longest match, "y.d.e.f" with empty data, the caller needs to
+ /// perform additional steps.
+ ///
+ /// This version of \c find() method is templated to allow the caller
+ /// to specify a "hook" at nodes that give a partial match.
+ /// When the search encounters a node with data that partially matches
+ /// \c name (i.e. node's name is a superdomain of \c name) and has
+ /// enabled callback (via the \c RBNode::enableCallback() method), if
+ /// \c callback is non \c NULL then the callback function is called
+ /// with the argument of a reference to the node and the given
+ /// callback argument (\c callback_arg). The template parameter specifies
+ /// the type of the callback argument.
+ /// The callback function returns either \c true or \c false, meaning
+ /// the search should stop or continue, respectively.
+ /// If the return value is \c true the search stops immediately at the
+ /// node even if there could be a longer matching name below it.
+ /// In reality, this convoluted callback rule is specifically intended
+ /// to be used to handle a zone cut (delegation) at a name search inside
+ /// a zone, and won't be used in any other cases.
+ /// Other applications of the tree won't need callbacks, and they should
+ /// use the non templated version of the \c find() method.
+ ///
+ /// Since the expected usage of callback is very limited, we do not
+ /// generalize the interface so that it can be an arbitrary functions or
+ /// functor objects in favor of simplicity and efficiency.
+ ///
+ /// This method involves operations on names that can throw an exception.
+ /// If that happens the exception will be propagated to the caller.
+ /// The callback function should generally not throw an exception, but
+ /// if it throws, the exception will be propagated to the caller.
+ ///
/// \param name Target to be found
- /// \param node Point to the node when the return vaule is \c not
- /// NOTFOUND, if the return value is NOTFOUND, the value of node is
- /// \c unknown
- Result find(const isc::dns::Name& name, RBNode<T>** node) const;
- Result find(const isc::dns::Name& name, const RBNode<T>** node) const;
+ /// \param node On success (either \c EXACTMATCH or \c PARTIALMATCH)
+ /// it will store a pointer to the matching node
+ /// \param callback If non \c NULL, a call back function to be called
+ /// at "delegation" nodes (see above).
+ /// \param callback_arg A caller supplied argument to be passed to
+ /// \c callback.
+ ///
+ /// \return \c EXACTMATCH A node that whose name is equal to \c name is
+ /// found. \c *node will be set to point to that node.
+ /// \return \c PARTIALMATCH There is a no exact match, but a superdomain
+ /// of \c name exists. \c node will be set to point to the node whose
+ /// name is the longest among such superdomains.
+ /// \return \c NOTFOUND There is no exact or partial match against \c name
+ /// \c *node will be intact in this case.
+ template <typename CBARG>
+ Result find(const isc::dns::Name& name, RBNode<T>** node,
+ bool (*callback)(const RBNode<T>&, CBARG),
+ CBARG callback_arg) const;
+
+ /// Same as the other version, but the returned \c node will be immutable.
+ template <typename CBARG>
+ Result find(const isc::dns::Name& name, const RBNode<T>** node,
+ bool (*callback)(const RBNode<T>&, CBARG),
+ CBARG callback_arg) const;
+
+ /// Same as the templated version, but does not use callback.
+ ///
+ /// Applications except the zone implementation should generally use the
+ /// non templated version.
+ Result find(const isc::dns::Name& name, RBNode<T>** node) const {
+ return (find<void*>(name, node, NULL, NULL));
+ }
+
+ /// Same as the templated version, but does not use callback, and the
+ /// returned \c node will be immutable.
+ ///
+ /// In general, this version should be preferred over the other non
+ /// templated version, unless the caller knows it should modify the
+ /// returned node.
+ Result find(const isc::dns::Name& name, const RBNode<T>** node) const {
+ return (find<void*>(name, node, NULL, NULL));
+ }
/// \brief Get the total node count in the tree
/// the node count including the node created common suffix node,
@@ -289,10 +407,20 @@
/// - ALREADYEXIST means already has the node with the given name
//
/// \node To modify the data related with one name but not sure the name has
- /// inserted or not, it is better to call \code insert \endcode,instead of
- /// \code find() \endcode, in case the name isn't exist and needs to insert again
+ /// inserted or not, it is better to call \c insert,instead of
+ /// \c find(), in case the name isn't exist and needs to insert again
Result insert(const isc::dns::Name& name, RBNode<T>** inserted_node);
//@}
+
+ /// \brief Swaps two tree's contents.
+ ///
+ /// This acts the same as many std::*.swap functions, exchanges the
+ /// contents. This doesn't throw anything.
+ void swap(RBTree<T>& other) {
+ std::swap(root_, other.root_);
+ std::swap(NULLNODE, other.NULLNODE);
+ std::swap(node_count_, other.node_count_);
+ }
private:
/// \name RBTree balance functions
@@ -315,8 +443,11 @@
/// and node will points to c.b.a
/// \note parameter up now is not used by any funciton, but we are gonna
/// need it soon to implement function like remove
+ template <typename CBARG>
Result findHelper(const isc::dns::Name& name, const RBNode<T>** up,
- RBNode<T>** node) const;
+ RBNode<T>** node,
+ bool (*callback)(const RBNode<T>&, CBARG),
+ CBARG callback_arg) const;
void dumpTreeHelper(std::ostream& os, const RBNode<T>* node,
unsigned int depth) const;
/// for indent purpose, add certian mount empty charachter to output stream
@@ -380,30 +511,39 @@
--node_count_;
}
-template <typename T>
+template <typename T> template <typename CBARG>
typename RBTree<T>::Result
-RBTree<T>::find(const isc::dns::Name& name, RBNode<T>** node) const {
+RBTree<T>::find(const isc::dns::Name& name, RBNode<T>** node,
+ bool (*callback)(const RBNode<T>&, CBARG),
+ CBARG callback_arg) const
+{
const RBNode<T>* up_node = NULLNODE;
- return (findHelper(name, &up_node, node));
-}
-
-template <typename T>
+ return (findHelper(name, &up_node, node, callback, callback_arg));
+}
+
+template <typename T> template <typename CBARG>
typename RBTree<T>::Result
-RBTree<T>::find(const isc::dns::Name& name, const RBNode<T>** node) const {
+RBTree<T>::find(const isc::dns::Name& name, const RBNode<T>** node,
+ bool (*callback)(const RBNode<T>&, CBARG),
+ CBARG callback_arg) const
+{
const RBNode<T>* up_node;
RBNode<T>* target_node;
const typename RBTree<T>::Result ret =
- findHelper(name, &up_node, &target_node);
+ findHelper(name, &up_node, &target_node, callback, callback_arg);
if (ret != NOTFOUND) {
*node = target_node;
}
return (ret);
}
-template <typename T>
+template <typename T> template <typename CBARG>
typename RBTree<T>::Result
-RBTree<T>::findHelper(const isc::dns::Name& target_name, const RBNode<T>** up_node,
- RBNode<T>** target) const
+RBTree<T>::findHelper(const isc::dns::Name& target_name,
+ const RBNode<T>** up_node,
+ RBNode<T>** target,
+ bool (*callback)(const RBNode<T>&, CBARG),
+ CBARG callback_arg) const
{
using namespace helper;
@@ -431,12 +571,17 @@
node = (compare_result.getOrder() < 0) ?
node->left_ : node->right_;
} else if (relation == isc::dns::NameComparisonResult::SUBDOMAIN) {
- *up_node = node;
- name = name - node->name_;
if (!node->isEmpty()) {
ret = RBTree<T>::PARTIALMATCH;
*target = node;
+ if (callback != NULL && node->callback_required_) {
+ if ((callback)(*node, callback_arg)) {
+ break;
+ }
+ }
}
+ *up_node = node;
+ name = name - node->name_;
node = node->down_;
} else {
break;
@@ -531,6 +676,7 @@
// after the RBNode creation
std::auto_ptr<RBNode<T> > down_node(new RBNode<T>(sub_name));
std::swap(node.data_, down_node->data_);
+ std::swap(node.callback_required_, down_node->callback_required_);
down_node->down_ = node.down_;
node.name_ = base_name;
node.down_ = down_node.get();
Modified: branches/trac448/src/lib/datasrc/tests/memory_datasrc_unittest.cc
==============================================================================
--- branches/trac448/src/lib/datasrc/tests/memory_datasrc_unittest.cc (original)
+++ branches/trac448/src/lib/datasrc/tests/memory_datasrc_unittest.cc Mon Jan 3 19:27:41 2011
@@ -16,6 +16,8 @@
#include <dns/name.h>
#include <dns/rrclass.h>
+#include <dns/rrttl.h>
+#include <dns/masterload.h>
#include <datasrc/memory_datasrc.h>
@@ -25,6 +27,9 @@
using namespace isc::datasrc;
namespace {
+// Commonly used result codes (Who should write the prefix all the time)
+using result::SUCCESS;
+using result::EXIST;
class MemoryDataSrcTest : public ::testing::Test {
protected:
@@ -136,13 +141,94 @@
MemoryZoneTest() :
class_(RRClass::IN()),
origin_("example.org"),
- zone_(class_, origin_)
- { }
+ ns_name_("ns.example.org"),
+ child_ns_name_("child.example.org"),
+ child_glue_name_("ns.child.example.org"),
+ grandchild_ns_name_("grand.child.example.org"),
+ grandchild_glue_name_("ns.grand.child.example.org"),
+ zone_(class_, origin_),
+ rr_out_(new RRset(Name("example.com"), class_, RRType::A(),
+ RRTTL(300))),
+ rr_ns_(new RRset(origin_, class_, RRType::NS(), RRTTL(300))),
+ rr_ns_a_(new RRset(ns_name_, class_, RRType::A(), RRTTL(300))),
+ rr_ns_aaaa_(new RRset(ns_name_, class_, RRType::AAAA(), RRTTL(300))),
+ rr_a_(new RRset(origin_, class_, RRType::A(), RRTTL(300))),
+ rr_child_ns_(new RRset(child_ns_name_, class_, RRType::NS(),
+ RRTTL(300))),
+ rr_child_glue_(new RRset(child_glue_name_, class_, RRType::A(),
+ RRTTL(300))),
+ rr_grandchild_ns_(new RRset(grandchild_ns_name_, class_, RRType::NS(),
+ RRTTL(300))),
+ rr_grandchild_glue_(new RRset(grandchild_glue_name_, class_,
+ RRType::AAAA(), RRTTL(300)))
+ {
+ }
// Some data to test with
- RRClass class_;
- Name origin_;
+ const RRClass class_;
+ const Name origin_, ns_name_, child_ns_name_, child_glue_name_,
+ grandchild_ns_name_, grandchild_glue_name_;
// The zone to torture by tests
MemoryZone zone_;
+
+ /*
+ * Some RRsets to put inside the zone.
+ * They are empty, but the MemoryZone does not have a reason to look
+ * inside anyway. We will check it finds them and does not change
+ * the pointer.
+ */
+ ConstRRsetPtr
+ // Out of zone RRset
+ rr_out_,
+ // NS of example.org
+ rr_ns_,
+ // A of ns.example.org
+ rr_ns_a_,
+ // AAAA of ns.example.org
+ rr_ns_aaaa_,
+ // A of example.org
+ rr_a_;
+ ConstRRsetPtr rr_child_ns_; // NS of a child domain (for delegation)
+ ConstRRsetPtr rr_child_glue_; // glue RR of the child domain
+ ConstRRsetPtr rr_grandchild_ns_; // NS below a zone cut (unusual)
+ ConstRRsetPtr rr_grandchild_glue_; // glue RR below a deeper zone cut
+
+ /**
+ * \brief Test one find query to the zone.
+ *
+ * Asks a query to the zone and checks it does not throw and returns
+ * expected results. It returns nothing, it just signals failures
+ * to GTEST.
+ *
+ * \param name The name to ask for.
+ * \param rrtype The RRType to ask of.
+ * \param result The expected code of the result.
+ * \param check_answer Should a check against equality of the answer be
+ * done?
+ * \param answer The expected rrset, if any should be returned.
+ * \param zone Check different MemoryZone object than zone_ (if NULL,
+ * uses zone_)
+ */
+ void findTest(const Name& name, const RRType& rrtype, Zone::Result result,
+ bool check_answer = true,
+ const ConstRRsetPtr& answer = ConstRRsetPtr(),
+ MemoryZone *zone = NULL,
+ Zone::FindOptions options = Zone::FIND_DEFAULT)
+ {
+ if (!zone) {
+ zone = &zone_;
+ }
+ // The whole block is inside, because we need to check the result and
+ // we can't assign to FindResult
+ EXPECT_NO_THROW({
+ Zone::FindResult find_result(zone->find(name, rrtype,
+ options));
+ // Check it returns correct answers
+ EXPECT_EQ(result, find_result.code);
+ if (check_answer) {
+ EXPECT_EQ(answer, find_result.rrset);
+ }
+ });
+ }
};
/**
@@ -151,8 +237,172 @@
* Takes the created zone and checks its properties they are the same
* as passed parameters.
*/
-TEST_F(MemoryZoneTest, Constructor) {
+TEST_F(MemoryZoneTest, constructor) {
ASSERT_EQ(class_, zone_.getClass());
ASSERT_EQ(origin_, zone_.getOrigin());
}
-}
+/**
+ * \brief Test adding.
+ *
+ * We test that it throws at the correct moments and the correct exceptions.
+ * And we test the return value.
+ */
+TEST_F(MemoryZoneTest, add) {
+ // This one does not belong to this zone
+ EXPECT_THROW(zone_.add(rr_out_), MemoryZone::OutOfZone);
+ // Test null pointer
+ EXPECT_THROW(zone_.add(ConstRRsetPtr()), MemoryZone::NullRRset);
+
+ // Now put all the data we have there. It should throw nothing
+ EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_)));
+ EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_a_)));
+ EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_aaaa_)));
+ EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_a_)));
+
+ // Try putting there something twice, it should be rejected
+ EXPECT_NO_THROW(EXPECT_EQ(EXIST, zone_.add(rr_ns_)));
+ EXPECT_NO_THROW(EXPECT_EQ(EXIST, zone_.add(rr_ns_a_)));
+}
+
+// Test adding child zones and zone cut handling
+TEST_F(MemoryZoneTest, delegationNS) {
+ // add in-zone data
+ EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_)));
+
+ // install a zone cut
+ EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_child_ns_)));
+
+ // below the zone cut
+ findTest(Name("www.child.example.org"), RRType::A(), Zone::DELEGATION,
+ true, rr_child_ns_);
+
+ // at the zone cut
+ findTest(Name("child.example.org"), RRType::A(), Zone::DELEGATION,
+ true, rr_child_ns_);
+ findTest(Name("child.example.org"), RRType::NS(), Zone::DELEGATION,
+ true, rr_child_ns_);
+
+ // finding NS for the apex (origin) node. This must not be confused
+ // with delegation due to the existence of an NS RR.
+ findTest(origin_, RRType::NS(), Zone::SUCCESS, true, rr_ns_);
+
+ // unusual case of "nested delegation": the highest cut should be used.
+ EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_grandchild_ns_)));
+ findTest(Name("www.grand.child.example.org"), RRType::A(),
+ Zone::DELEGATION, true, rr_child_ns_); // note: !rr_grandchild_ns_
+}
+
+TEST_F(MemoryZoneTest, glue) {
+ // install zone data:
+ // a zone cut
+ EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_child_ns_)));
+ // glue for this cut
+ EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_child_glue_)));
+ // a nested zone cut (unusual)
+ EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_grandchild_ns_)));
+ // glue under the deeper zone cut
+ EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_grandchild_glue_)));
+
+ // by default glue is hidden due to the zone cut
+ findTest(child_glue_name_, RRType::A(), Zone::DELEGATION, true,
+ rr_child_ns_);
+
+
+ // If we do it in the "glue OK" mode, we should find the exact match.
+ findTest(child_glue_name_, RRType::A(), Zone::SUCCESS, true,
+ rr_child_glue_, NULL, Zone::FIND_GLUE_OK);
+
+ // glue OK + NXRRSET case
+ findTest(child_glue_name_, RRType::AAAA(), Zone::NXRRSET, true,
+ ConstRRsetPtr(), NULL, Zone::FIND_GLUE_OK);
+
+ // glue OK + NXDOMAIN case
+ findTest(Name("www.child.example.org"), RRType::A(), Zone::DELEGATION,
+ true, rr_child_ns_, NULL, Zone::FIND_GLUE_OK);
+
+ // TODO:
+ // glue name would match a wildcard under a zone cut: wildcard match
+ // shouldn't happen under a cut and result must be PARTIALMATCH
+ // (This case cannot be tested yet)
+
+ // nested cut case. The glue should be found.
+ findTest(grandchild_glue_name_, RRType::AAAA(), Zone::SUCCESS,
+ true, rr_grandchild_glue_, NULL, Zone::FIND_GLUE_OK);
+
+ // A non-existent name in nested cut. This should result in delegation
+ // at the highest zone cut.
+ findTest(Name("www.grand.child.example.org"), RRType::TXT(),
+ Zone::DELEGATION, true, rr_child_ns_, NULL, Zone::FIND_GLUE_OK);
+}
+
+// Test adding DNAMEs and resulting delegation handling
+// Listing ideas only for now
+TEST_F(MemoryZoneTest, delegationDNAME) {
+ // apex DNAME: allowed by spec. No DNAME delegation at the apex;
+ // descendants are subject to delegation.
+
+ // Other cases of NS and DNAME mixture are prohibited.
+ // BIND 9 doesn't reject such cases at load time, however.
+
+ // DNAME and ordinary types (allowed by spec)
+}
+
+/**
+ * \brief Test searching.
+ *
+ * Check it finds or does not find correctly and does not throw exceptions.
+ * \todo This doesn't do any kind of CNAME and so on. If it isn't
+ * directly there, it just tells it doesn't exist.
+ */
+TEST_F(MemoryZoneTest, find) {
+ // Fill some data inside
+ // Now put all the data we have there. It should throw nothing
+ EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_)));
+ EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_a_)));
+ EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_aaaa_)));
+ EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_a_)));
+
+ // These two should be successful
+ findTest(origin_, RRType::NS(), Zone::SUCCESS, true, rr_ns_);
+ findTest(ns_name_, RRType::A(), Zone::SUCCESS, true, rr_ns_a_);
+
+ // These domain exist but don't have the provided RRType
+ findTest(origin_, RRType::AAAA(), Zone::NXRRSET);
+ findTest(ns_name_, RRType::NS(), Zone::NXRRSET);
+
+ // These domains don't exist (and one is out of the zone)
+ findTest(Name("nothere.example.org"), RRType::A(), Zone::NXDOMAIN);
+ findTest(Name("example.net"), RRType::A(), Zone::NXDOMAIN);
+}
+
+TEST_F(MemoryZoneTest, load) {
+ // Put some data inside the zone
+ EXPECT_NO_THROW(EXPECT_EQ(result::SUCCESS, zone_.add(rr_ns_)));
+ // Loading with different origin should fail
+ EXPECT_THROW(zone_.load(TEST_DATA_DIR "/root.zone"), MasterLoadError);
+ // See the original data is still there, survived the exception
+ findTest(origin_, RRType::NS(), Zone::SUCCESS, true, rr_ns_);
+ // Create correct zone
+ MemoryZone rootzone(class_, Name("."));
+ // Try putting something inside
+ EXPECT_NO_THROW(EXPECT_EQ(result::SUCCESS, rootzone.add(rr_ns_aaaa_)));
+ // Load the zone. It should overwrite/remove the above RRset
+ EXPECT_NO_THROW(rootzone.load(TEST_DATA_DIR "/root.zone"));
+
+ // Now see there are some rrsets (we don't look inside, though)
+ findTest(Name("."), RRType::SOA(), Zone::SUCCESS, false, ConstRRsetPtr(),
+ &rootzone);
+ findTest(Name("."), RRType::NS(), Zone::SUCCESS, false, ConstRRsetPtr(),
+ &rootzone);
+ findTest(Name("a.root-servers.net."), RRType::A(), Zone::SUCCESS, false,
+ ConstRRsetPtr(), &rootzone);
+ // But this should no longer be here
+ findTest(ns_name_, RRType::AAAA(), Zone::NXDOMAIN, true, ConstRRsetPtr(),
+ &rootzone);
+
+ // Try loading zone that is wrong in a different way
+ EXPECT_THROW(zone_.load(TEST_DATA_DIR "/duplicate_rrset.zone"),
+ MasterLoadError);
+}
+
+}
Modified: branches/trac448/src/lib/datasrc/tests/rbtree_unittest.cc
==============================================================================
--- branches/trac448/src/lib/datasrc/tests/rbtree_unittest.cc (original)
+++ branches/trac448/src/lib/datasrc/tests/rbtree_unittest.cc Mon Jan 3 19:27:41 2011
@@ -169,6 +169,61 @@
EXPECT_EQ(Name("q"), rbtnode->getName());
}
+bool
+testCallback(const RBNode<int>&, bool* callack_checker) {
+ *callack_checker = true;
+ return (false);
+}
+
+TEST_F(RBTreeTest, callback) {
+ // by default callback isn't enabled
+ EXPECT_EQ(RBTree<int>::SUCCEED, rbtree.insert(Name("callback.example"),
+ &rbtnode));
+ rbtnode->setData(RBNode<int>::NodeDataPtr(new int(1)));
+ EXPECT_FALSE(rbtnode->isCallbackEnabled());
+
+ // enable/re-disable callback
+ rbtnode->enableCallback();
+ EXPECT_TRUE(rbtnode->isCallbackEnabled());
+ rbtnode->disableCallback();
+ EXPECT_FALSE(rbtnode->isCallbackEnabled());
+
+ // enable again for subsequent tests
+ rbtnode->enableCallback();
+ // add more levels below and above the callback node for partial match.
+ RBNode<int>* subrbtnode;
+ EXPECT_EQ(RBTree<int>::SUCCEED, rbtree.insert(Name("sub.callback.example"),
+ &subrbtnode));
+ subrbtnode->setData(RBNode<int>::NodeDataPtr(new int(2)));
+ RBNode<int>* parentrbtnode;
+ EXPECT_EQ(RBTree<int>::ALREADYEXIST, rbtree.insert(Name("example"),
+ &parentrbtnode));
+ // the chilld/parent nodes shouldn't "inherit" the callback flag.
+ // "rbtnode" may be invalid due to the insertion, so we need to re-find
+ // it.
+ EXPECT_EQ(RBTree<int>::EXACTMATCH, rbtree.find(Name("callback.example"),
+ &rbtnode));
+ EXPECT_TRUE(rbtnode->isCallbackEnabled());
+ EXPECT_FALSE(subrbtnode->isCallbackEnabled());
+ EXPECT_FALSE(parentrbtnode->isCallbackEnabled());
+
+ // check if the callback is called from find()
+ bool callback_called = false;
+ EXPECT_EQ(RBTree<int>::EXACTMATCH,
+ rbtree.find(Name("sub.callback.example"), &crbtnode,
+ testCallback, &callback_called));
+ EXPECT_TRUE(callback_called);
+
+ // enable callback at the parent node, but it doesn't have data so
+ // the callback shouldn't be called.
+ parentrbtnode->enableCallback();
+ callback_called = false;
+ EXPECT_EQ(RBTree<int>::EXACTMATCH,
+ rbtree.find(Name("callback.example"), &crbtnode,
+ testCallback, &callback_called));
+ EXPECT_FALSE(callback_called);
+}
+
TEST_F(RBTreeTest, dumpTree) {
std::ostringstream str;
std::ostringstream str2;
@@ -176,4 +231,34 @@
str2 << "tree has 13 node(s)\nb. (black)\n a. (black)\n NULL\n NULL\n d.e.f. (black)[invisible] \n begin down from d.e.f.\n w.y. (black)[invisible] \n begin down from w.y.\n p. (black)\n o. (red)\n NULL\n NULL\n q. (red)\n NULL\n NULL\n end down from w.y.\n x. (red)\n NULL\n NULL\n z. (red)\n begin down from z.\n j. (black)\n NULL\n NULL\n end down from z.\n NULL\n NULL\n end down from d.e.f.\n c. (red)\n NULL\n NULL\n g.h. (red)\n begin down from g.h.\n i. (black)\n
NULL\n NULL\n end down from g.h.\n NULL\n NULL\n";
EXPECT_EQ(str.str(), str2.str());
}
-}
+
+TEST_F(RBTreeTest, swap) {
+ // Store info about the first tree
+ std::ostringstream str1;
+ rbtree.dumpTree(str1);
+ size_t count1(rbtree.getNodeCount());
+
+ // Create second one and store state
+ RBTree<int> tree2;
+ RBNode<int>* node;
+ tree2.insert(Name("second"), &node);
+ std::ostringstream str2;
+ tree2.dumpTree(str2);
+
+ // Swap them
+ ASSERT_NO_THROW(tree2.swap(rbtree));
+
+ // Check their sizes
+ ASSERT_EQ(1, rbtree.getNodeCount());
+ ASSERT_EQ(count1, tree2.getNodeCount());
+
+ // And content
+ std::ostringstream out;
+ rbtree.dumpTree(out);
+ ASSERT_EQ(str2.str(), out.str());
+ out.str("");
+ tree2.dumpTree(out);
+ ASSERT_EQ(str1.str(), out.str());
+}
+
+}
Modified: branches/trac448/src/lib/datasrc/zone.h
==============================================================================
--- branches/trac448/src/lib/datasrc/zone.h (original)
+++ branches/trac448/src/lib/datasrc/zone.h Mon Jan 3 19:27:41 2011
@@ -98,6 +98,16 @@
{}
const Result code;
const isc::dns::ConstRRsetPtr rrset;
+ };
+
+ /// Find options.
+ ///
+ /// The option values are used as a parameter for \c find().
+ /// These are values of a bitmask type. Bitwise operations can be
+ /// performed on these values to express compound options.
+ enum FindOptions {
+ FIND_DEFAULT = 0, ///< The default options
+ FIND_GLUE_OK = 1 ///< Allow search under a zone cut
};
///
@@ -150,6 +160,17 @@
/// - If the search name matches a delegation point of DNAME, it returns
/// the code of \c DNAME and that DNAME RR.
///
+ /// The \c options parameter specifies customized behavior of the search.
+ /// Their semantics is as follows:
+ /// - \c GLUE_OK Allow search under a zone cut. By default the search
+ /// will stop once it encounters a zone cut. If this option is specified
+ /// it remembers information about the highest zone cut and continues
+ /// the search until it finds an exact match for the given name or it
+ /// detects there is no exact match. If an exact match is found,
+ /// RRsets for that name are searched just like the normal case;
+ /// otherwise, if the search has encountered a zone cut, \c DELEGATION
+ /// with the information of the highest zone cut will be returned.
+ ///
/// A derived version of this method may involve internal resource
/// allocation, especially for constructing the resulting RRset, and may
/// throw an exception if it fails.
@@ -162,9 +183,12 @@
///
/// \param name The domain name to be searched for.
/// \param type The RR type to be searched for.
+ /// \param options The search options.
/// \return A \c FindResult object enclosing the search result (see above).
virtual FindResult find(const isc::dns::Name& name,
- const isc::dns::RRType& type) const = 0;
+ const isc::dns::RRType& type,
+ const FindOptions options
+ = FIND_DEFAULT) const = 0;
//@}
};
@@ -177,4 +201,8 @@
}
}
-#endif
+#endif // __ZONE_H
+
+// Local Variables:
+// mode: c++
+// End:
Modified: branches/trac448/src/lib/datasrc/zonetable.cc
==============================================================================
--- branches/trac448/src/lib/datasrc/zonetable.cc (original)
+++ branches/trac448/src/lib/datasrc/zonetable.cc Mon Jan 3 19:27:41 2011
@@ -89,6 +89,8 @@
// Can Not Happen
default:
assert(0);
+ // Because of warning
+ return (FindResult(result::NOTFOUND, ConstZonePtr()));
}
// Can Not Happen (remember, NOTFOUND is handled)
Modified: branches/trac448/src/lib/datasrc/zonetable.h
==============================================================================
--- branches/trac448/src/lib/datasrc/zonetable.h (original)
+++ branches/trac448/src/lib/datasrc/zonetable.h Mon Jan 3 19:27:41 2011
@@ -25,7 +25,7 @@
namespace dns {
class Name;
class RRClass;
-};
+}
namespace datasrc {
Modified: branches/trac448/src/lib/dns/python/tests/Makefile.am
==============================================================================
--- branches/trac448/src/lib/dns/python/tests/Makefile.am (original)
+++ branches/trac448/src/lib/dns/python/tests/Makefile.am Mon Jan 3 19:27:41 2011
@@ -1,3 +1,4 @@
+PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
PYTESTS = edns_python_test.py
PYTESTS += message_python_test.py
PYTESTS += messagerenderer_python_test.py
@@ -22,14 +23,17 @@
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
check-local:
+if ENABLE_PYTHON_COVERAGE
+ touch $(abs_top_srcdir)/.coverage
+ rm -f .coverage
+ ${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
env PYTHONPATH=$(abs_top_srcdir)/src/lib/dns/.libs:$(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:$(abs_top_builddir)/src/lib/dns/tests/testdata \
$(LIBRARY_PATH_PLACEHOLDER) \
- $(PYCOVERAGE) $(abs_srcdir)/$$pytest || exit ; \
+ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
Modified: branches/trac448/src/lib/python/isc/cc/tests/Makefile.am
==============================================================================
--- branches/trac448/src/lib/python/isc/cc/tests/Makefile.am (original)
+++ branches/trac448/src/lib/python/isc/cc/tests/Makefile.am Mon Jan 3 19:27:41 2011
@@ -1,16 +1,21 @@
+PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
+
PYTESTS = message_test.py data_test.py session_test.py
# NOTE: test_session.py is to be run manually, so not automated.
EXTRA_DIST = $(PYTESTS)
EXTRA_DIST += sendcmd.py
EXTRA_DIST += test_session.py
-# 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
check-local:
+if ENABLE_PYTHON_COVERAGE
+ touch $(abs_top_srcdir)/.coverage
+ rm -f .coverage
+ ${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
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 || exit ; \
+ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
Modified: branches/trac448/src/lib/python/isc/config/module_spec.py
==============================================================================
--- branches/trac448/src/lib/python/isc/config/module_spec.py (original)
+++ branches/trac448/src/lib/python/isc/config/module_spec.py Mon Jan 3 19:27:41 2011
@@ -339,13 +339,14 @@
# check if there are items in our data that are not in the
# specification
- for item_name in data:
- found = False
- for spec_item in module_spec:
- if spec_item["item_name"] == item_name:
- found = True
- if not found:
- if errors != None:
- errors.append("unknown item " + item_name)
- validated = False
+ if data is not None:
+ for item_name in data:
+ found = False
+ for spec_item in module_spec:
+ if spec_item["item_name"] == item_name:
+ found = True
+ if not found:
+ if errors != None:
+ errors.append("unknown item " + item_name)
+ validated = False
return validated
Modified: branches/trac448/src/lib/python/isc/config/tests/Makefile.am
==============================================================================
--- branches/trac448/src/lib/python/isc/config/tests/Makefile.am (original)
+++ branches/trac448/src/lib/python/isc/config/tests/Makefile.am Mon Jan 3 19:27:41 2011
@@ -1,16 +1,20 @@
+PYCOVERAGE_RUN=@PYCOVERAGE_RUN@
PYTESTS = ccsession_test.py cfgmgr_test.py config_data_test.py
PYTESTS += module_spec_test.py
EXTRA_DIST = $(PYTESTS)
EXTRA_DIST += unittest_fakesession.py
-# 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
check-local:
+if ENABLE_PYTHON_COVERAGE
+ touch $(abs_top_srcdir)/.coverage
+ rm -f .coverage
+ ${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
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/tests/testdata \
CONFIG_WR_TESTDATA_PATH=$(abs_top_builddir)/src/lib/config/tests/testdata \
- $(PYCOVERAGE) $(abs_srcdir)/$$pytest || exit ; \
+ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
Modified: branches/trac448/src/lib/python/isc/config/tests/module_spec_test.py
==============================================================================
--- branches/trac448/src/lib/python/isc/config/tests/module_spec_test.py (original)
+++ branches/trac448/src/lib/python/isc/config/tests/module_spec_test.py Mon Jan 3 19:27:41 2011
@@ -109,6 +109,9 @@
return dd.validate_command(cmd_name, params)
def test_command_validation(self):
+ # tests for a command that doesn't take an argument
+ self.assertEqual(True, self.read_spec_file("spec2.spec").validate_command("shutdown", None));
+ self.assertEqual(False, self.read_spec_file("spec2.spec").validate_command("shutdown", '{"val": 1}'));
self.assertEqual(True, self.validate_command_params("spec27.spec", "data22_1.data", 'cmd1'))
self.assertEqual(False, self.validate_command_params("spec27.spec", "data22_2.data",'cmd1'))
self.assertEqual(False, self.validate_command_params("spec27.spec", "data22_3.data", 'cmd1'))
@@ -321,6 +324,7 @@
}]
errors = []
+ self.assertEqual(True, isc.config.module_spec._validate_spec_list(spec, True, None, None))
self.assertEqual(False, isc.config.module_spec._validate_spec_list(spec, True, { 'does_not_exist': 1 }, None))
self.assertEqual(False, isc.config.module_spec._validate_spec_list(spec, True, { 'does_not_exist': 1 }, errors))
self.assertEqual(['unknown item does_not_exist'], errors)
Modified: branches/trac448/src/lib/python/isc/datasrc/tests/Makefile.am
==============================================================================
--- branches/trac448/src/lib/python/isc/datasrc/tests/Makefile.am (original)
+++ branches/trac448/src/lib/python/isc/datasrc/tests/Makefile.am Mon Jan 3 19:27:41 2011
@@ -1,12 +1,16 @@
+PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
PYTESTS = master_test.py
EXTRA_DIST = $(PYTESTS)
-# 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
check-local:
+if ENABLE_PYTHON_COVERAGE
+ touch $(abs_top_srcdir)/.coverage
+ rm -f .coverage
+ ${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
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/python/isc/log \
- $(PYCOVERAGE) $(abs_srcdir)/$$pytest || exit ; \
+ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
Modified: branches/trac448/src/lib/python/isc/log/tests/Makefile.am
==============================================================================
--- branches/trac448/src/lib/python/isc/log/tests/Makefile.am (original)
+++ branches/trac448/src/lib/python/isc/log/tests/Makefile.am Mon Jan 3 19:27:41 2011
@@ -1,12 +1,16 @@
+PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
PYTESTS = log_test.py
EXTRA_DIST = $(PYTESTS)
-# 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
check-local:
+if ENABLE_PYTHON_COVERAGE
+ touch $(abs_top_srcdir)/.coverage
+ rm -f .coverage
+ ${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
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/python/isc/log \
- $(PYCOVERAGE) $(abs_srcdir)/$$pytest || exit ; \
+ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
Modified: branches/trac448/src/lib/python/isc/net/tests/Makefile.am
==============================================================================
--- branches/trac448/src/lib/python/isc/net/tests/Makefile.am (original)
+++ branches/trac448/src/lib/python/isc/net/tests/Makefile.am Mon Jan 3 19:27:41 2011
@@ -1,12 +1,16 @@
+PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
PYTESTS = addr_test.py parse_test.py
EXTRA_DIST = $(PYTESTS)
-# 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
check-local:
+if ENABLE_PYTHON_COVERAGE
+ touch $(abs_top_srcdir)/.coverage
+ rm -f .coverage
+ ${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
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 \
- $(PYCOVERAGE) $(abs_srcdir)/$$pytest || exit ; \
+ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
Modified: branches/trac448/src/lib/python/isc/notify/tests/Makefile.am
==============================================================================
--- branches/trac448/src/lib/python/isc/notify/tests/Makefile.am (original)
+++ branches/trac448/src/lib/python/isc/notify/tests/Makefile.am Mon Jan 3 19:27:41 2011
@@ -1,3 +1,4 @@
+PYCOVERAGE_RUN=@PYCOVERAGE_RUN@
PYTESTS = notify_out_test.py
EXTRA_DIST = $(PYTESTS)
@@ -8,13 +9,16 @@
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
check-local:
+if ENABLE_PYTHON_COVERAGE
+ touch $(abs_top_srcdir)/.coverage
+ rm -f .coverage
+ ${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
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 || exit ; \
+ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
Modified: branches/trac448/src/lib/python/isc/util/tests/Makefile.am
==============================================================================
--- branches/trac448/src/lib/python/isc/util/tests/Makefile.am (original)
+++ branches/trac448/src/lib/python/isc/util/tests/Makefile.am Mon Jan 3 19:27:41 2011
@@ -1,12 +1,16 @@
+PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
PYTESTS = process_test.py socketserver_mixin_test.py
EXTRA_DIST = $(PYTESTS)
-# 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
check-local:
+if ENABLE_PYTHON_COVERAGE
+ touch $(abs_top_srcdir)/.coverage
+ rm -f .coverage
+ ${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
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 \
- $(PYCOVERAGE) $(abs_srcdir)/$$pytest || exit ; \
+ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
Modified: branches/trac448/src/lib/testutils/testdata/Makefile.am
==============================================================================
--- branches/trac448/src/lib/testutils/testdata/Makefile.am (original)
+++ branches/trac448/src/lib/testutils/testdata/Makefile.am Mon Jan 3 19:27:41 2011
@@ -4,6 +4,7 @@
BUILT_SOURCES += iqueryresponse_fromWire.wire multiquestion_fromWire.wire
BUILT_SOURCES += queryBadEDNS_fromWire.wire shortanswer_fromWire.wire
BUILT_SOURCES += simplequery_fromWire.wire simpleresponse_fromWire.wire
+BUILT_SOURCES += iquery_fromWire.wire iquery_response_fromWire.wire
# NOTE: keep this in sync with real file listing
# so is included in tarball
@@ -18,6 +19,8 @@
EXTRA_DIST += shortresponse_fromWire
EXTRA_DIST += simplequery_fromWire.spec
EXTRA_DIST += simpleresponse_fromWire.spec
+EXTRA_DIST += iquery_fromWire.spec iquery_response_fromWire.spec
+EXTRA_DIST += example.com.zone example.net.zone example.org.zone example.zone
EXTRA_DIST += example.com
EXTRA_DIST += example.sqlite3
More information about the bind10-changes
mailing list