[svn] commit: r4018 - in /branches/trac347: ./ doc/ 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/ src/bin/cmdctl/ src/bin/loadzone/ src/bin/msgq/ src/bin/recurse/ src/bin/stats/ src/bin/usermgr/ src/bin/xfrin/ src/bin/xfrout/ src/bin/zonemgr/ src/lib/ src/lib/asiolink/ src/lib/asiolink/tests/ src/lib/cc/ src/lib/config/ src/lib/config/tests/ src/lib/config/tests/testdata/ src/lib/datasrc/ src/lib/datasrc/tests/ src/lib/dns/ src/lib/dns/tests/ src/lib/dns/tests/testdata/ src/lib/log/ src/lib/nsas/ src/lib/python/isc/cc/ src/lib/python/isc/cc/tests/ src/lib/python/isc/config/ src/lib/python/isc/config/tests/ src/lib/python/isc/utils/ src/lib/testutils/
BIND 10 source code commits
bind10-changes at lists.isc.org
Mon Dec 27 11:50:02 UTC 2010
Author: y-aharen
Date: Mon Dec 27 11:50:02 2010
New Revision: 4018
Log:
Merged with trunk r3685
Added:
branches/trac347/src/bin/auth/config.cc
- copied unchanged from r4017, trunk/src/bin/auth/config.cc
branches/trac347/src/bin/auth/config.h
- copied unchanged from r4017, trunk/src/bin/auth/config.h
branches/trac347/src/bin/auth/query.cc
- copied unchanged from r4017, trunk/src/bin/auth/query.cc
branches/trac347/src/bin/auth/query.h
- copied unchanged from r4017, trunk/src/bin/auth/query.h
branches/trac347/src/bin/auth/tests/config_unittest.cc
- copied unchanged from r4017, trunk/src/bin/auth/tests/config_unittest.cc
branches/trac347/src/bin/auth/tests/query_unittest.cc
- copied unchanged from r4017, trunk/src/bin/auth/tests/query_unittest.cc
branches/trac347/src/bin/recurse/
- copied from r4017, trunk/src/bin/recurse/
branches/trac347/src/bin/recurse/Makefile.am
- copied unchanged from r4017, trunk/src/bin/recurse/Makefile.am
branches/trac347/src/bin/recurse/recurse.spec.pre.in
- copied unchanged from r4017, trunk/src/bin/recurse/recurse.spec.pre.in
branches/trac347/src/lib/asiolink/
- copied from r4017, trunk/src/lib/asiolink/
branches/trac347/src/lib/config/tests/testdata/data22_9.data
- copied unchanged from r4017, trunk/src/lib/config/tests/testdata/data22_9.data
branches/trac347/src/lib/datasrc/memory_datasrc.cc
- copied unchanged from r4017, trunk/src/lib/datasrc/memory_datasrc.cc
branches/trac347/src/lib/datasrc/memory_datasrc.h
- copied unchanged from r4017, trunk/src/lib/datasrc/memory_datasrc.h
branches/trac347/src/lib/datasrc/rbtree.h
- copied unchanged from r4017, trunk/src/lib/datasrc/rbtree.h
branches/trac347/src/lib/datasrc/result.h
- copied unchanged from r4017, trunk/src/lib/datasrc/result.h
branches/trac347/src/lib/datasrc/tests/memory_datasrc_unittest.cc
- copied unchanged from r4017, trunk/src/lib/datasrc/tests/memory_datasrc_unittest.cc
branches/trac347/src/lib/datasrc/tests/rbtree_unittest.cc
- copied unchanged from r4017, trunk/src/lib/datasrc/tests/rbtree_unittest.cc
branches/trac347/src/lib/datasrc/zone.h
- copied unchanged from r4017, trunk/src/lib/datasrc/zone.h
branches/trac347/src/lib/dns/masterload.cc
- copied unchanged from r4017, trunk/src/lib/dns/masterload.cc
branches/trac347/src/lib/dns/masterload.h
- copied unchanged from r4017, trunk/src/lib/dns/masterload.h
branches/trac347/src/lib/dns/tests/masterload_unittest.cc
- copied unchanged from r4017, trunk/src/lib/dns/tests/masterload_unittest.cc
branches/trac347/src/lib/dns/tests/testdata/masterload.txt
- copied unchanged from r4017, trunk/src/lib/dns/tests/testdata/masterload.txt
branches/trac347/src/lib/log/
- copied from r4017, trunk/src/lib/log/
branches/trac347/src/lib/nsas/
- copied from r4017, trunk/src/lib/nsas/
branches/trac347/src/lib/testutils/
- copied from r4017, trunk/src/lib/testutils/
Removed:
branches/trac347/src/bin/auth/asio_link.cc
branches/trac347/src/bin/auth/asio_link.h
branches/trac347/src/bin/auth/tests/asio_link_unittest.cc
branches/trac347/src/bin/auth/tests/testdata/
branches/trac347/src/lib/python/isc/utils/
Modified:
branches/trac347/ (props changed)
branches/trac347/ChangeLog
branches/trac347/Makefile.am
branches/trac347/README
branches/trac347/configure.ac
branches/trac347/doc/Doxyfile
branches/trac347/doc/guide/bind10-guide.html
branches/trac347/doc/guide/bind10-guide.xml
branches/trac347/src/bin/auth/Makefile.am
branches/trac347/src/bin/auth/auth.spec.pre.in
branches/trac347/src/bin/auth/auth_srv.cc
branches/trac347/src/bin/auth/auth_srv.h
branches/trac347/src/bin/auth/benchmarks/Makefile.am
branches/trac347/src/bin/auth/benchmarks/query_bench.cc
branches/trac347/src/bin/auth/change_user.cc
branches/trac347/src/bin/auth/common.h
branches/trac347/src/bin/auth/main.cc
branches/trac347/src/bin/auth/tests/Makefile.am
branches/trac347/src/bin/auth/tests/auth_srv_unittest.cc
branches/trac347/src/bin/bind10/bind10.py.in (contents, props changed)
branches/trac347/src/bin/bind10/run_bind10.sh.in
branches/trac347/src/bin/bind10/tests/bind10_test.py
branches/trac347/src/bin/bindctl/bindcmd.py
branches/trac347/src/bin/bindctl/bindctl-source.py.in
branches/trac347/src/bin/bindctl/run_bindctl.sh.in
branches/trac347/src/bin/cmdctl/run_b10-cmdctl.sh.in
branches/trac347/src/bin/loadzone/run_loadzone.sh.in
branches/trac347/src/bin/msgq/msgq.py.in
branches/trac347/src/bin/msgq/run_msgq.sh.in
branches/trac347/src/bin/stats/run_b10-stats.sh.in
branches/trac347/src/bin/stats/run_b10-stats_stub.sh.in
branches/trac347/src/bin/usermgr/run_b10-cmdctl-usermgr.sh.in
branches/trac347/src/bin/xfrin/b10-xfrin.xml
branches/trac347/src/bin/xfrin/run_b10-xfrin.sh.in
branches/trac347/src/bin/xfrout/b10-xfrout.8
branches/trac347/src/bin/xfrout/b10-xfrout.xml
branches/trac347/src/bin/xfrout/run_b10-xfrout.sh.in
branches/trac347/src/bin/xfrout/xfrout.spec.pre.in
branches/trac347/src/bin/zonemgr/run_b10-zonemgr.sh.in
branches/trac347/src/lib/Makefile.am
branches/trac347/src/lib/asiolink/asiolink.cc
branches/trac347/src/lib/asiolink/asiolink.h
branches/trac347/src/lib/asiolink/tests/asiolink_unittest.cc
branches/trac347/src/lib/cc/session.cc
branches/trac347/src/lib/config/module_spec.cc
branches/trac347/src/lib/config/tests/module_spec_unittests.cc
branches/trac347/src/lib/config/tests/testdata/Makefile.am
branches/trac347/src/lib/config/tests/testdata/data22_8.data
branches/trac347/src/lib/datasrc/Makefile.am
branches/trac347/src/lib/datasrc/data_source.h
branches/trac347/src/lib/datasrc/static_datasrc.cc
branches/trac347/src/lib/datasrc/tests/Makefile.am
branches/trac347/src/lib/datasrc/tests/datasrc_unittest.cc
branches/trac347/src/lib/datasrc/tests/static_unittest.cc
branches/trac347/src/lib/datasrc/tests/zonetable_unittest.cc
branches/trac347/src/lib/datasrc/zonetable.cc
branches/trac347/src/lib/datasrc/zonetable.h
branches/trac347/src/lib/dns/Makefile.am
branches/trac347/src/lib/dns/buffer.h
branches/trac347/src/lib/dns/message.h
branches/trac347/src/lib/dns/messagerenderer.h
branches/trac347/src/lib/dns/tests/Makefile.am
branches/trac347/src/lib/dns/tests/testdata/Makefile.am
branches/trac347/src/lib/dns/tests/testdata/edns_toWire4.spec
branches/trac347/src/lib/dns/tests/tsigkey_unittest.cc
branches/trac347/src/lib/dns/tests/unittest_util.cc
branches/trac347/src/lib/dns/tests/unittest_util.h
branches/trac347/src/lib/python/isc/cc/data.py
branches/trac347/src/lib/python/isc/cc/tests/data_test.py
branches/trac347/src/lib/python/isc/config/ccsession.py
branches/trac347/src/lib/python/isc/config/config_data.py
branches/trac347/src/lib/python/isc/config/module_spec.py
branches/trac347/src/lib/python/isc/config/tests/ccsession_test.py
branches/trac347/src/lib/python/isc/config/tests/config_data_test.py
branches/trac347/src/lib/python/isc/config/tests/module_spec_test.py
Modified: branches/trac347/ChangeLog
==============================================================================
--- branches/trac347/ChangeLog (original)
+++ branches/trac347/ChangeLog Mon Dec 27 11:50:02 2010
@@ -10,6 +10,110 @@
counters to b10-stats immediately.
(Trac #347, svn rxxxx)
+ 138. [func]* jinmei
+ b10-auth: added a configuration interface to support in memory
+ data sources. For example, the following command to bindctl
+ will configure a memory data source containing the "example.com"
+ zone with the zone file named "example.com.zone":
+ > config set Auth/datasources/ [{"type": "memory", "zones": \
+ [{"origin": "example.com", "file": "example.com.zone"}]}]
+ By default, the memory data source is disabled; it must be
+ configured explicitly. To disable it again, specify a null list
+ for Auth/datasources:
+ > config set Auth/datasources/ []
+ Notes: it's currently for class IN only. The zone files are not
+ actually loaded into memory yet (which will soon be implemented).
+ This is an experimental feature and the syntax may change in
+ future versions.
+ (Trac #446, svn r3998)
+
+ 137. [bug] jreed
+ Fix run_*.sh scripts that are used for development testing
+ so they use a msgq socket file in the build tree.
+ (Trac #226, svn r3989)
+
+ 136. [bug] jelte
+ bindctl (and the configuration manager in general) now no longer
+ accepts 'unknown' data; i.e. data for modules that it does not know
+ about, or configuration items that are not specified in the .spec
+ files.
+ (Trac #202, svn r3967)
+
+ 135. [func] each
+ Add b10-recurse. This is an example recursive server that
+ currently does forwarding only and no caching.
+ (Trac #327, svn r3903)
+
+ 134. [func] vorner
+ b10-recurse supports timeouts and retries in forwarder mode.
+ (Trac #401, svn r3660)
+
+ 133. [func] vorner
+ New temporary logging function available in isc::log. It is used by
+ b10-recurse.
+ (Trac #393, r3602)
+
+ 132. [func] vorner
+ The b10-recurse is configured through config manager.
+ It has "listen_on" and "forward_addresses" options.
+ (Trac #389, r3448)
+
+ 131. [func] feng, jerry
+ src/lib/datasrc: Introduced two template classes RBTree and RBNode
+ to provide the generic map with domain name as key and anything as
+ the value. Because of some unresolved design issue, the new classes
+ are only intended to be used by memory zone and zone table.
+ (Trac #397, svn r3890)
+
+ 130. [func] jerry
+ src/lib/datasrc: Introduced a new class MemoryDataSrc to provide
+ the general interface for memory data source. For the initial
+ implementation, we don't make it a derived class of AbstractDataSrc
+ because the interface is so different(we'll eventually consider this
+ as part of the generalization work).
+ (Trac #422, svn r3866)
+
+ 129. [func] jinmei
+ src/lib/dns: Added new functions masterLoad() for loading master
+ zone files. The initial implementation can only parse a limited
+ form of master files, but BIND 9's named-compilezone can convert
+ any valid zone file into the acceptable form.
+ (Trac #423, svn r3857)
+
+ 128. [build] vorner
+ Test for query name = '.', type = DS to authoritative nameserver
+ for root zone was added.
+ (Trac #85, svn r3836)
+
+ 127. [bug] stephen
+ During normal operation process termination and resurrection messages
+ are now output regardless of the state of the verbose flag.
+ (Trac #229, svn r3828)
+
+ 126. [func] stephen, vorner, ocean
+ The Nameserver Address Store (NSAS) component has been added. It takes
+ care of choosing an IP address of a nameserver when a zone needs to be
+ contacted.
+ (Trac #356, Trac #408, svn r3823)
+
+bind10-devel-20101201 released on December 01, 2010
+
+ 125. [func] jelte
+ Added support for addressing individual list items in bindctl
+ configuration commands; If you have an element that is a list, you
+ can use foo[X] to address a specific item, where X is an integer
+ (starting at 0)
+ (Trac #405, svn r3739)
+
+ 124. [bug] jreed
+ Fix some wrong version reporting. Now also show the version
+ for the component and BIND 10 suite. (Trac #302, svn r3696)
+
+ 123. [bug] jelte
+ src/bin/bindctl printed values had the form of python literals
+ (e.g. 'True'), while the input requires valid JSON (e.g. 'true').
+ Output changed to JSON format for consistency. (svn r3694)
+
122. [func] stephen
src/bin/bind10: Added configuration options to Boss to determine
whether to start the authoritative server, recursive server (or
@@ -33,13 +137,14 @@
Note: this fix is incomplete and the loadzone would still be
confused if the owner name is a syntactically indistinguishable
from a TTL specification. This is part of a more general issue
- and will be addressed in Trac #413. (Trac #411, svn r3599)
+ and will be addressed in Trac #413. (Trac #411, svn r3599)
118. [func] jinmei
- src/lib/dns: changed the interface of AbstractRRset::getRdataIterator()
- so that the internal cursor would point to the first RDATA
- automatically. This will be a more intuitive and less error prone
- behavior. This is a backward compatible change. (Trac #410, r3595)
+ src/lib/dns: changed the interface of
+ AbstractRRset::getRdataIterator() so that the internal
+ cursor would point to the first RDATA automatically. This
+ will be a more intuitive and less error prone behavior.
+ This is a backward compatible change. (Trac #410, r3595)
117. [func] jinmei
src/lib/datasrc: added new zone and zone table classes for the
@@ -75,15 +180,13 @@
Add one mixin class to override the naive serve_forever() provided
in python library socketserver. Instead of polling for shutdwon
every poll_interval seconds, one socketpair is used to wake up
- the waiting server.(Trac #352, svn r3366)
+ the waiting server. (Trac #352, svn r3366)
111. [bug]* zhanglikun, Michal Vaner
- Make sure process xfrin/xfrout/zonemgr/cmdctl can be stoped
+ Make sure process xfrin/xfrout/zonemgr/cmdctl can be stopped
properly when user enter "ctrl+c" or 'Boss shutdown' command
- through bindctl.
-
- The ZonemgrRefresh.run_timer and NotifyOut.dispatcher spawn
- a thread themselves.
+ through bindctl. The ZonemgrRefresh.run_timer and
+ NotifyOut.dispatcher spawn a thread themselves.
(Trac #335, svn r3273)
110. [func] Michal Vaner
@@ -95,7 +198,7 @@
109. [func] naokikambe
Added the initial version of the stats module for the statistics
feature of BIND 10, which supports the restricted features and
- items and reports via bindctl command (Trac #191, r3218)
+ items and reports via bindctl command. (Trac #191, r3218)
Added the document of the stats module, which is about how stats
module collects the data (Trac #170, [wiki:StatsModule])
@@ -122,11 +225,11 @@
104. [bug] jerry
bin/zonemgr: zonemgr should be attempting to refresh expired zones.
(Trac #336, r3139)
-
+
103. [bug] jerry
lib/python/isc/log: Fixed an issue with python logging,
- python log shouldn't die with OSError.(Trac #267, r3137)
-
+ python log shouldn't die with OSError. (Trac #267, r3137)
+
102. [build] jinmei
Disable threads in ASIO to minimize build time dependency.
(Trac #345, r3100)
@@ -179,7 +282,7 @@
93. [bug] jinmei
lib/datasrc: A DS query could crash the library (and therefore,
e.g. the authoritative server) if some RR of the same apex name
- is stored in the hot spot cache. (Trac #307, svn r2923)
+ is stored in the hot spot cache. (Trac #307, svn r2923)
92. [func]* jelte
libdns_python (the python wrappers for libdns++) has been renamed
@@ -359,7 +462,7 @@
66. [bug] each
Check for duplicate RRsets before inserting data into a message
section; this, among other things, will prevent multiple copies
- of the same CNAME from showing up when there's a loop. (Trac #69,
+ of the same CNAME from showing up when there's a loop. (Trac #69,
svn r2350)
65. [func] shentingting
@@ -481,7 +584,7 @@
#205, svn r1957)
44. [build] jreed
- Install headers for libdns and libexception. (Trac #68,
+ Install headers for libdns and libexception. (Trac #68,
svn r1941)
43. [func] jelte
@@ -489,7 +592,7 @@
42. [func] jelte
lib/python/isc/config: Make temporary file with python
- tempfile module instead of manual with fixed name. (Trac
+ tempfile module instead of manual with fixed name. (Trac
#184, svn r1859)
41. [func] jelte
@@ -497,7 +600,7 @@
40. [build] jreed
Report detected features and configure settings at end of
- configure output. (svn r1836)
+ configure output. (svn r1836)
39. [func]* each
Renamed libauth to libdatasrc.
@@ -510,7 +613,7 @@
(Trac #135, #151, #134, svn r1797)
37. [build] jinmei
- Check for the availability of python-config. (Trac #159,
+ Check for the availability of python-config. (Trac #159,
svn r1794)
36. [func] shane
@@ -555,7 +658,7 @@
27. [build]
Add missing copyright license statements to various source
- files. (svn r1750)
+ files. (svn r1750)
26. [func]
Use PACKAGE_STRING (name + version) from config.h instead
Modified: branches/trac347/Makefile.am
==============================================================================
--- branches/trac347/Makefile.am (original)
+++ branches/trac347/Makefile.am Mon Dec 27 11:50:02 2010
@@ -37,7 +37,7 @@
\*_unittest.cc \
\*_unittests.h \
--output report.info
- $(GENHTML) -o coverage report.info
+ $(GENHTML) --legend -o coverage report.info
coverage: clean-coverage perform-coverage report-coverage
Modified: branches/trac347/README
==============================================================================
--- branches/trac347/README (original)
+++ branches/trac347/README Mon Dec 27 11:50:02 2010
@@ -17,8 +17,9 @@
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, and a new libdns++ library for C++
-with a python wrapper.
+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/
Modified: branches/trac347/configure.ac
==============================================================================
--- branches/trac347/configure.ac (original)
+++ branches/trac347/configure.ac Mon Dec 27 11:50:02 2010
@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.59])
-AC_INIT(bind10-devel, 20101013, bind10-dev at isc.org)
+AC_INIT(bind10-devel, 20101201, bind10-dev at isc.org)
AC_CONFIG_SRCDIR(README)
AM_INIT_AUTOMAKE
AC_CONFIG_HEADERS([config.h])
@@ -195,17 +195,50 @@
# specify the default warning flags in CXXFLAGS and let specific modules
# "override" the default.
+# This may be used to try linker flags.
+AC_DEFUN([BIND10_CXX_TRY_FLAG], [
+ AC_MSG_CHECKING([whether $CXX supports $1])
+
+ bind10_save_CXXFLAGS="$CXXFLAGS"
+ CXXFLAGS="$CXXFLAGS $1"
+
+ AC_LINK_IFELSE([int main(void){ return 0;} ],
+ [bind10_cxx_flag=yes], [bind10_cxx_flag=no])
+ CXXFLAGS="$bind10_save_CXXFLAGS"
+
+ if test "x$bind10_cxx_flag" = "xyes"; then
+ ifelse([$2], , :, [$2])
+ else
+ ifelse([$3], , :, [$3])
+ fi
+
+ AC_MSG_RESULT([$bind10_cxx_flag])
+])
+
werror_ok=0
# SunStudio compiler requires special compiler options for boost
# (http://blogs.sun.com/sga/entry/boost_mini_howto)
if test "$SUNCXX" = "yes"; then
CXXFLAGS="$CXXFLAGS -library=stlport4 -features=tmplife -features=tmplrefstatic"
-fi
+MULTITHREADING_FLAG="-mt"
+fi
+
+BIND10_CXX_TRY_FLAG(-Wno-missing-field-initializers,
+ [WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG="-Wno-missing-field-initializers"])
+AC_SUBST(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
# gcc specific settings:
if test "X$GXX" = "Xyes"; then
B10_CXXFLAGS="-Wall -Wextra -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare"
+case "$host" in
+*-solaris*)
+ MULTITHREADING_FLAG=-pthreads
+ ;;
+*)
+ MULTITHREADING_FLAG=-pthread
+ ;;
+esac
# Certain versions of gcc (g++) have a bug that incorrectly warns about
# the use of anonymous name spaces even if they're closed in a single
@@ -318,10 +351,66 @@
BOOST_INCLUDES="-I${boost_include_path}"
CPPFLAGS="$CPPFLAGS $BOOST_INCLUDES"
fi
-AC_CHECK_HEADERS([boost/shared_ptr.hpp boost/foreach.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)
+
+# Using boost::mutex can result in requiring libboost_thread with older
+# versions of Boost. We'd like to avoid relying on a compiled Boost library
+# whenever possible, so we need to check for it step by step.
+#
+# NOTE: another fix of this problem is to simply require newer versions of
+# boost. If we choose that solution we should simplify the following tricky
+# checks accordingly and all Makefile.am's that refer to NEED_LIBBOOST_THREAD.
+AC_MSG_CHECKING(for boost::mutex)
+CPPFLAGS_SAVES="$CPPFLAGS"
+LIBS_SAVES="$LIBS"
+CPPFLAGS="$BOOST_INCLUDES $CPPFLAGS $MULTITHREADING_FLAG"
+need_libboost_thread=0
+need_sunpro_workaround=0
+AC_TRY_LINK([
+#include <boost/thread.hpp>
+],[
+boost::mutex m;
+],
+ [ AC_MSG_RESULT(yes (without libboost_thread)) ],
+
+ # there is one specific problem with SunStudio 5.10
+ # where including boost/thread causes a compilation failure
+ # There is a workaround in boost but it checks the version not being 5.10
+ # This will probably be fixed in the future, in which case this
+ # is only a temporary workaround
+ [ AC_TRY_LINK([
+#if defined(__SUNPRO_CC) && __SUNPRO_CC == 0x5100
+#undef __SUNPRO_CC
+#define __SUNPRO_CC 0x5090
+#endif
+#include <boost/thread.hpp>
+],[
+boost::mutex m;
+],
+ [ AC_MSG_RESULT(yes (with SUNOS workaround))
+ need_sunpro_workaround=1 ],
+ [ LIBS=" $LIBS -lboost_thread"
+ AC_TRY_LINK([
+#include <boost/thread.hpp>
+],[
+boost::mutex m;
+],
+ [ AC_MSG_RESULT(yes (with libboost_thread))
+ need_libboost_thread=1 ],
+ [ AC_MSG_RESULT(no)
+ AC_MSG_ERROR([boost::mutex cannot be linked in this build environment.
+Perhaps you are using an older version of Boost that requires libboost_thread for the mutex support, which does not appear to be available.
+You may want to check the availability of the library or to upgrade Boost.])
+ ])])])
+CPPFLAGS="$CPPFLAGS_SAVES"
+LIBS="$LIBS_SAVES"
+AM_CONDITIONAL(NEED_LIBBOOST_THREAD, test $need_libboost_thread = 1)
+if test $need_sunpro_workaround = 1; then
+ AC_DEFINE([NEED_SUNPRO_WORKAROUND], [], [Need boost sunstudio workaround])
+fi
#
# Check availability of gtest, which will be used for unit tests.
@@ -387,6 +476,8 @@
PTHREAD_LDFLAGS=
AC_CHECK_LIB(pthread, pthread_create,[ PTHREAD_LDFLAGS=-lpthread ], [])
AC_SUBST(PTHREAD_LDFLAGS)
+
+AC_SUBST(MULTITHREADING_FLAG)
#
# ASIO: we extensively use it as the C++ event management module.
@@ -465,9 +556,9 @@
src/bin/msgq/tests/Makefile
src/bin/auth/Makefile
src/bin/auth/tests/Makefile
- src/bin/auth/tests/testdata/Makefile
src/bin/auth/benchmarks/Makefile
src/bin/recurse/Makefile
+ src/bin/recurse/tests/Makefile
src/bin/xfrin/Makefile
src/bin/xfrin/tests/Makefile
src/bin/xfrout/Makefile
@@ -484,6 +575,8 @@
src/bin/usermgr/Makefile
src/bin/tests/Makefile
src/lib/Makefile
+ src/lib/asiolink/Makefile
+ src/lib/asiolink/tests/Makefile
src/lib/bench/Makefile
src/lib/bench/example/Makefile
src/lib/bench/tests/Makefile
@@ -518,6 +611,11 @@
src/lib/datasrc/Makefile
src/lib/datasrc/tests/Makefile
src/lib/xfr/Makefile
+ src/lib/log/Makefile
+ src/lib/testutils/Makefile
+ src/lib/testutils/testdata/Makefile
+ src/lib/nsas/Makefile
+ src/lib/nsas/tests/Makefile
])
AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
src/bin/cfgmgr/tests/b10-cfgmgr_test.py
@@ -532,9 +630,8 @@
src/bin/xfrout/xfrout.spec.pre
src/bin/xfrout/tests/xfrout_test
src/bin/xfrout/run_b10-xfrout.sh
- src/bin/recurse/recurse.py
src/bin/recurse/recurse.spec.pre
- src/bin/recurse/run_b10-recurse.sh
+ src/bin/recurse/spec_config.h.pre
src/bin/zonemgr/zonemgr.py
src/bin/zonemgr/zonemgr.spec.pre
src/bin/zonemgr/tests/zonemgr_test
Modified: branches/trac347/doc/Doxyfile
==============================================================================
--- branches/trac347/doc/Doxyfile (original)
+++ branches/trac347/doc/Doxyfile Mon Dec 27 11:50:02 2010
@@ -568,7 +568,7 @@
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
-INPUT = ../src/lib/cc ../src/lib/config ../src/lib/dns ../src/lib/exceptions ../src/lib/datasrc ../src/bin/auth ../src/lib/bench
+INPUT = ../src/lib/cc ../src/lib/config ../src/lib/dns ../src/lib/exceptions ../src/lib/datasrc ../src/bin/auth ../src/lib/bench ../src/lib/log ../src/lib/asiolink/ ../src/lib/nsas
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
Modified: branches/trac347/doc/guide/bind10-guide.html
==============================================================================
--- branches/trac347/doc/guide/bind10-guide.html (original)
+++ branches/trac347/doc/guide/bind10-guide.html Mon Dec 27 11:50:02 2010
@@ -128,7 +128,7 @@
libraries, to build BIND 10 from source code.
</p></div><p>
Building from source code requires the Boost
- build-time headers. At least Boost version 1.34 is required.
+ build-time headers. At least Boost version 1.35 is required.
</p><p>
Modified: branches/trac347/doc/guide/bind10-guide.xml
==============================================================================
--- branches/trac347/doc/guide/bind10-guide.xml (original)
+++ branches/trac347/doc/guide/bind10-guide.xml Mon Dec 27 11:50:02 2010
@@ -274,7 +274,7 @@
<para>
Building from source code requires the Boost
- build-time headers. At least Boost version 1.34 is required.
+ build-time headers. At least Boost version 1.35 is required.
<!-- TODO: we don't check for this version -->
<!-- NOTE: jreed has tested with 1.34, 1.38, and 1.41. -->
</para>
Modified: branches/trac347/src/bin/auth/Makefile.am
==============================================================================
--- branches/trac347/src/bin/auth/Makefile.am (original)
+++ branches/trac347/src/bin/auth/Makefile.am Mon Dec 27 11:50:02 2010
@@ -3,8 +3,9 @@
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
-AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
-AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/cc -I$(top_builddir)/src/lib/cc
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/asiolink
+AM_CPPFLAGS += -I$(top_builddir)/src/lib/asiolink
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CXXFLAGS = $(B10_CXXFLAGS)
@@ -33,30 +34,12 @@
spec_config.h: spec_config.h.pre
$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" spec_config.h.pre >$@
-# This is a wrapper library solely used for b10-auth. The ASIO header files
-# have some code fragments that would hit gcc's unused-parameter warning,
-# which would make the build fail with -Werror (our default setting).
-# We don't want to lower the warning level for our own code just for ASIO,
-# so as a workaround we extract the ASIO related code into a separate library,
-# only for which we accept the unused-parameter warning.
-lib_LIBRARIES = libasio_link.a
-libasio_link_a_SOURCES = asio_link.cc asio_link.h
-# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
-# B10_CXXFLAGS)
-libasio_link_a_CXXFLAGS = $(AM_CXXFLAGS)
-if USE_GXX
-libasio_link_a_CXXFLAGS += -Wno-unused-parameter
-endif
-if USE_CLANGPP
-# Same for clang++, but we need to turn off -Werror completely.
-libasio_link_a_CXXFLAGS += -Wno-error
-endif
-libasio_link_a_CPPFLAGS = $(AM_CPPFLAGS)
-
-BUILT_SOURCES = spec_config.h
+BUILT_SOURCES = spec_config.h
pkglibexec_PROGRAMS = b10-auth
-b10_auth_SOURCES = auth_srv.cc auth_srv.h
+b10_auth_SOURCES = query.cc query.h
+b10_auth_SOURCES += auth_srv.cc auth_srv.h
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
@@ -65,7 +48,7 @@
b10_auth_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
b10_auth_LDADD += $(top_builddir)/src/lib/cc/libcc.la
b10_auth_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
-b10_auth_LDADD += libasio_link.a
+b10_auth_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
b10_auth_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
b10_auth_LDADD += $(SQLITE_LIBS)
Modified: branches/trac347/src/bin/auth/auth.spec.pre.in
==============================================================================
--- branches/trac347/src/bin/auth/auth.spec.pre.in (original)
+++ branches/trac347/src/bin/auth/auth.spec.pre.in Mon Dec 27 11:50:02 2010
@@ -7,6 +7,51 @@
"item_type": "string",
"item_optional": true,
"item_default": "@@LOCALSTATEDIR@@/@PACKAGE@/zone.sqlite3"
+ },
+ { "item_name": "datasources",
+ "item_type": "list",
+ "item_optional": true,
+ "item_default": [],
+ "list_item_spec": {
+ "item_name": "list_element",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "type",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ },
+ { "item_name": "class",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "IN"
+ },
+ { "item_name": "zones",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec": {
+ "item_name": "list_element",
+ "item_type": "map",
+ "item_optional": true,
+ "map_item_spec": [
+ { "item_name": "origin",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ },
+ { "item_name": "file",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ }
+ ]
+ }
+ }
+ ]
+ }
}
],
"commands": [
Modified: branches/trac347/src/bin/auth/auth_srv.cc
==============================================================================
--- branches/trac347/src/bin/auth/auth_srv.cc (original)
+++ branches/trac347/src/bin/auth/auth_srv.cc Mon Dec 27 11:50:02 2010
@@ -14,12 +14,20 @@
// $Id$
+#include <config.h>
+
#include <netinet/in.h>
#include <algorithm>
#include <cassert>
#include <iostream>
#include <vector>
+
+#include <asiolink/asiolink.h>
+
+#include <config/ccsession.h>
+
+#include <cc/data.h>
#include <exceptions/exceptions.h>
@@ -34,22 +42,19 @@
#include <dns/rrset.h>
#include <dns/rrttl.h>
#include <dns/message.h>
-#include <config/ccsession.h>
-#include <cc/data.h>
-#include <exceptions/exceptions.h>
#include <datasrc/query.h>
#include <datasrc/data_source.h>
+#include <datasrc/memory_datasrc.h>
#include <datasrc/static_datasrc.h>
#include <datasrc/sqlite3_datasrc.h>
-#include <cc/data.h>
-
#include <xfr/xfrout_client.h>
#include <auth/common.h>
+#include <auth/config.h>
#include <auth/auth_srv.h>
-#include <auth/asio_link.h>
+#include <auth/query.h>
#include <auth/statistics.h>
using namespace std;
@@ -58,11 +63,12 @@
using namespace isc::cc;
using namespace isc::datasrc;
using namespace isc::dns;
+using namespace isc::auth;
using namespace isc::dns::rdata;
using namespace isc::data;
using namespace isc::config;
using namespace isc::xfr;
-using namespace asio_link;
+using namespace asiolink;
class AuthSrvImpl {
private:
@@ -74,47 +80,54 @@
~AuthSrvImpl();
isc::data::ConstElementPtr setDbFile(isc::data::ConstElementPtr config);
- bool processNormalQuery(const IOMessage& io_message, Message& message,
- MessageRenderer& response_renderer);
- bool processAxfrQuery(const IOMessage& io_message, Message& message,
- MessageRenderer& response_renderer);
- bool processNotify(const IOMessage& io_message, Message& message,
- MessageRenderer& response_renderer);
+ bool processNormalQuery(const IOMessage& io_message, MessagePtr message,
+ OutputBufferPtr buffer);
+ bool processAxfrQuery(const IOMessage& io_message, MessagePtr message,
+ OutputBufferPtr buffer);
+ bool processNotify(const IOMessage& io_message, MessagePtr message,
+ OutputBufferPtr buffer);
+
+ /// Currently non-configurable, but will be.
+ static const uint16_t DEFAULT_LOCAL_UDPSIZE = 4096;
+
+ /// These members are public because AuthSrv accesses them directly.
+ ModuleCCSession* config_session_;
+ bool verbose_mode_;
+ AbstractSession* xfrin_session_;
+
+ /// In-memory data source. Currently class IN only for simplicity.
+ const RRClass memory_datasrc_class_;
+ AuthSrv::MemoryDataSrcPtr memory_datasrc_;
+
+ /// Hot spot cache
+ isc::datasrc::HotCache cache_;
+
+ /// Query counters for statistics
+ AuthCounters counters_;
+private:
std::string db_file_;
- ModuleCCSession* config_session_;
+
MetaDataSrc data_sources_;
/// We keep a pointer to the currently running sqlite datasource
/// so that we can specifically remove that one should the database
/// file change
ConstDataSrcPtr cur_datasrc_;
- bool verbose_mode_;
-
- AbstractSession* xfrin_session_;
-
bool xfrout_connected_;
AbstractXfroutClient& xfrout_client_;
- /// Currently non-configurable, but will be.
- static const uint16_t DEFAULT_LOCAL_UDPSIZE = 4096;
-
- /// Hot spot cache
- isc::datasrc::HotCache cache_;
-
- /// Query counters for statistics
- AuthCounters counters_;
-private:
/// Increment query counter
- void incCounter(int protocol);
+ void incCounter(const int protocol);
};
AuthSrvImpl::AuthSrvImpl(const bool use_cache,
AbstractXfroutClient& xfrout_client) :
config_session_(NULL), verbose_mode_(false),
xfrin_session_(NULL),
+ memory_datasrc_class_(RRClass::IN()),
+ counters_(verbose_mode_),
xfrout_connected_(false),
- xfrout_client_(xfrout_client),
- counters_(verbose_mode_)
+ xfrout_client_(xfrout_client)
{
// cur_datasrc_ is automatically initialized by the default constructor,
// effectively being an empty (sqlite) data source. once ccsession is up
@@ -134,60 +147,127 @@
}
}
+// This is a derived class of \c DNSLookup, to serve as a
+// callback in the asiolink module. It calls
+// AuthSrv::processMessage() on a single DNS message.
+class MessageLookup : public DNSLookup {
+public:
+ MessageLookup(AuthSrv* srv) : server_(srv) {}
+ virtual void operator()(const IOMessage& io_message, MessagePtr message,
+ OutputBufferPtr buffer, DNSServer* server) const
+ {
+ server_->processMessage(io_message, message, buffer, server);
+ }
+private:
+ 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.
+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_;
+};
+
+// This is a derived class of \c SimpleCallback, to serve
+// as a callback in the asiolink module. It checks for queued
+// configuration messages, and executes them if found.
+class ConfigChecker : public SimpleCallback {
+public:
+ ConfigChecker(AuthSrv* srv) : server_(srv) {}
+ virtual void operator()(const IOMessage&) const {
+ if (server_->getConfigSession()->hasQueuedMsgs()) {
+ server_->getConfigSession()->checkCommand();
+ }
+ }
+private:
+ AuthSrv* server_;
+};
+
AuthSrv::AuthSrv(const bool use_cache, AbstractXfroutClient& xfrout_client) :
- impl_(new AuthSrvImpl(use_cache, xfrout_client))
+ impl_(new AuthSrvImpl(use_cache, xfrout_client)),
+ checkin_(new ConfigChecker(this)),
+ dns_lookup_(new MessageLookup(this)),
+ dns_answer_(new MessageAnswer(this))
{}
AuthSrv::~AuthSrv() {
delete impl_;
+ delete checkin_;
+ delete dns_lookup_;
+ delete dns_answer_;
}
namespace {
class QuestionInserter {
public:
- QuestionInserter(Message* message) : message_(message) {}
+ QuestionInserter(MessagePtr message) : message_(message) {}
void operator()(const QuestionPtr question) {
message_->addQuestion(question);
}
- Message* message_;
+ MessagePtr message_;
};
void
-makeErrorMessage(Message& message, MessageRenderer& renderer,
+makeErrorMessage(MessagePtr message, OutputBufferPtr buffer,
const Rcode& rcode, const bool verbose_mode)
{
// extract the parameters that should be kept.
// XXX: with the current implementation, it's not easy to set EDNS0
// depending on whether the query had it. So we'll simply omit it.
- const qid_t qid = message.getQid();
- const bool rd = message.getHeaderFlag(Message::HEADERFLAG_RD);
- const bool cd = message.getHeaderFlag(Message::HEADERFLAG_CD);
- const Opcode& opcode = message.getOpcode();
+ const qid_t qid = message->getQid();
+ const bool rd = message->getHeaderFlag(Message::HEADERFLAG_RD);
+ const bool cd = message->getHeaderFlag(Message::HEADERFLAG_CD);
+ const Opcode& opcode = message->getOpcode();
vector<QuestionPtr> questions;
// If this is an error to a query or notify, we should also copy the
// question section.
if (opcode == Opcode::QUERY() || opcode == Opcode::NOTIFY()) {
- questions.assign(message.beginQuestion(), message.endQuestion());
- }
-
- message.clear(Message::RENDER);
- message.setQid(qid);
- message.setOpcode(opcode);
- message.setHeaderFlag(Message::HEADERFLAG_QR);
+ questions.assign(message->beginQuestion(), message->endQuestion());
+ }
+
+ message->clear(Message::RENDER);
+ message->setQid(qid);
+ message->setOpcode(opcode);
+ message->setHeaderFlag(Message::HEADERFLAG_QR);
if (rd) {
- message.setHeaderFlag(Message::HEADERFLAG_RD);
+ message->setHeaderFlag(Message::HEADERFLAG_RD);
}
if (cd) {
- message.setHeaderFlag(Message::HEADERFLAG_CD);
- }
- for_each(questions.begin(), questions.end(), QuestionInserter(&message));
- message.setRcode(rcode);
- message.toWire(renderer);
+ message->setHeaderFlag(Message::HEADERFLAG_CD);
+ }
+ for_each(questions.begin(), questions.end(), QuestionInserter(message));
+ message->setRcode(rcode);
+
+ MessageRenderer renderer(*buffer);
+ message->toWire(renderer);
if (verbose_mode) {
cerr << "[b10-auth] sending an error response (" <<
- renderer.getLength() << " bytes):\n" << message.toText() << endl;
+ renderer.getLength() << " bytes):\n" << message->toText() << endl;
}
}
}
@@ -232,99 +312,135 @@
return (impl_->config_session_);
}
-bool
-AuthSrv::processMessage(const IOMessage& io_message, Message& message,
- MessageRenderer& response_renderer)
+AuthSrv::ConstMemoryDataSrcPtr
+AuthSrv::getMemoryDataSrc(const RRClass& rrclass) const {
+ // XXX: for simplicity, we only support the IN class right now.
+ if (rrclass != impl_->memory_datasrc_class_) {
+ isc_throw(InvalidParameter,
+ "Memory data source is not supported for RR class "
+ << rrclass);
+ }
+ return (impl_->memory_datasrc_);
+}
+
+void
+AuthSrv::setMemoryDataSrc(const isc::dns::RRClass& rrclass,
+ MemoryDataSrcPtr memory_datasrc)
+{
+ // XXX: see above
+ if (rrclass != impl_->memory_datasrc_class_) {
+ isc_throw(InvalidParameter,
+ "Memory data source is not supported for RR class "
+ << rrclass);
+ }
+ if (impl_->verbose_mode_) {
+ if (!impl_->memory_datasrc_ && memory_datasrc) {
+ cerr << "[b10-auth] Memory data source is enabled for class "
+ << rrclass << endl;
+ } else if (impl_->memory_datasrc_ && !memory_datasrc) {
+ cerr << "[b10-auth] Memory data source is disabled for class "
+ << rrclass << endl;
+ }
+ }
+ impl_->memory_datasrc_ = memory_datasrc;
+}
+
+void
+AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
+ OutputBufferPtr buffer, DNSServer* server)
{
InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
// First, check the header part. If we fail even for the base header,
// just drop the message.
try {
- message.parseHeader(request_buffer);
+ message->parseHeader(request_buffer);
// Ignore all responses.
- if (message.getHeaderFlag(Message::HEADERFLAG_QR)) {
+ if (message->getHeaderFlag(Message::HEADERFLAG_QR)) {
if (impl_->verbose_mode_) {
cerr << "[b10-auth] received unexpected response, ignoring"
<< endl;
}
- return (false);
+ server->resume(false);
+ return;
}
} catch (const Exception& ex) {
- return (false);
- }
-
- // Parse the message. On failure, return an appropriate error.
+ if (impl_->verbose_mode_) {
+ cerr << "[b10-auth] DNS packet exception: " << ex.what() << endl;
+ }
+ server->resume(false);
+ return;
+ }
+
try {
- message.fromWire(request_buffer);
+ // Parse the message.
+ message->fromWire(request_buffer);
} catch (const DNSProtocolError& error) {
if (impl_->verbose_mode_) {
cerr << "[b10-auth] returning " << error.getRcode().toText()
<< ": " << error.what() << endl;
}
- makeErrorMessage(message, response_renderer, error.getRcode(),
+ makeErrorMessage(message, buffer, error.getRcode(),
impl_->verbose_mode_);
- return (true);
+ server->resume(true);
+ return;
} catch (const Exception& ex) {
if (impl_->verbose_mode_) {
cerr << "[b10-auth] returning SERVFAIL: " << ex.what() << endl;
}
- makeErrorMessage(message, response_renderer, Rcode::SERVFAIL(),
+ makeErrorMessage(message, buffer, Rcode::SERVFAIL(),
impl_->verbose_mode_);
- return (true);
+ server->resume(true);
+ return;
} // other exceptions will be handled at a higher layer.
if (impl_->verbose_mode_) {
- cerr << "[b10-auth] received a message:\n" << message.toText() << endl;
+ cerr << "[b10-auth] received a message:\n" << message->toText() << endl;
}
// Perform further protocol-level validation.
- if (message.getOpcode() == Opcode::NOTIFY()) {
- return (impl_->processNotify(io_message, message, response_renderer));
- } else if (message.getOpcode() != Opcode::QUERY()) {
+ bool sendAnswer = true;
+ if (message->getOpcode() == Opcode::NOTIFY()) {
+ sendAnswer = impl_->processNotify(io_message, message, buffer);
+ } else if (message->getOpcode() != Opcode::QUERY()) {
if (impl_->verbose_mode_) {
cerr << "[b10-auth] unsupported opcode" << endl;
}
- makeErrorMessage(message, response_renderer, Rcode::NOTIMP(),
+ makeErrorMessage(message, buffer, Rcode::NOTIMP(),
impl_->verbose_mode_);
- return (true);
- }
-
- if (message.getRRCount(Message::SECTION_QUESTION) != 1) {
- makeErrorMessage(message, response_renderer, Rcode::FORMERR(),
+ } else if (message->getRRCount(Message::SECTION_QUESTION) != 1) {
+ makeErrorMessage(message, buffer, Rcode::FORMERR(),
impl_->verbose_mode_);
- return (true);
- }
-
- ConstQuestionPtr question = *message.beginQuestion();
- const RRType &qtype = question->getType();
- if (qtype == RRType::AXFR()) {
- return (impl_->processAxfrQuery(io_message, message,
- response_renderer));
- } else if (qtype == RRType::IXFR()) {
- makeErrorMessage(message, response_renderer, Rcode::NOTIMP(),
- impl_->verbose_mode_);
- return (true);
} else {
- return (impl_->processNormalQuery(io_message, message,
- response_renderer));
- }
+ ConstQuestionPtr question = *message->beginQuestion();
+ const RRType &qtype = question->getType();
+ if (qtype == RRType::AXFR()) {
+ sendAnswer = impl_->processAxfrQuery(io_message, message, buffer);
+ } else if (qtype == RRType::IXFR()) {
+ makeErrorMessage(message, buffer, Rcode::NOTIMP(),
+ impl_->verbose_mode_);
+ } else {
+ sendAnswer = impl_->processNormalQuery(io_message, message, buffer);
+ }
+ }
+
+ server->resume(sendAnswer);
}
bool
-AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
- MessageRenderer& response_renderer)
+AuthSrvImpl::processNormalQuery(const IOMessage& io_message, MessagePtr message,
+ OutputBufferPtr buffer)
{
- ConstEDNSPtr remote_edns = message.getEDNS();
+ ConstEDNSPtr remote_edns = message->getEDNS();
const bool dnssec_ok = remote_edns && remote_edns->getDNSSECAwareness();
const uint16_t remote_bufsize = remote_edns ? remote_edns->getUDPSize() :
Message::DEFAULT_MAX_UDPSIZE;
- message.makeResponse();
- message.setHeaderFlag(Message::HEADERFLAG_AA);
- message.setRcode(Rcode::NOERROR());
+ message->makeResponse();
+ message->setHeaderFlag(Message::HEADERFLAG_AA);
+ message->setRcode(Rcode::NOERROR());
// Increment query counter.
incCounter(io_message.getSocket().getProtocol());
@@ -333,38 +449,48 @@
EDNSPtr local_edns = EDNSPtr(new EDNS());
local_edns->setDNSSECAwareness(dnssec_ok);
local_edns->setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
- message.setEDNS(local_edns);
+ message->setEDNS(local_edns);
}
try {
- Query query(message, cache_, dnssec_ok);
- data_sources_.doQuery(query);
+ // If a memory data source is configured call the separate
+ // Query::process()
+ const ConstQuestionPtr question = *message->beginQuestion();
+ if (memory_datasrc_ && memory_datasrc_class_ == question->getClass()) {
+ const RRType& qtype = question->getType();
+ const Name& qname = question->getName();
+ auth::Query(*memory_datasrc_, qname, qtype, *message).process();
+ } else {
+ datasrc::Query query(*message, cache_, dnssec_ok);
+ data_sources_.doQuery(query);
+ }
} catch (const Exception& ex) {
if (verbose_mode_) {
cerr << "[b10-auth] Internal error, returning SERVFAIL: " <<
ex.what() << endl;
}
- makeErrorMessage(message, response_renderer, Rcode::SERVFAIL(),
- verbose_mode_);
+ makeErrorMessage(message, buffer, Rcode::SERVFAIL(), verbose_mode_);
return (true);
}
+ MessageRenderer renderer(*buffer);
const bool udp_buffer =
(io_message.getSocket().getProtocol() == IPPROTO_UDP);
- response_renderer.setLengthLimit(udp_buffer ? remote_bufsize : 65535);
- message.toWire(response_renderer);
+ renderer.setLengthLimit(udp_buffer ? remote_bufsize : 65535);
+ message->toWire(renderer);
+
if (verbose_mode_) {
cerr << "[b10-auth] sending a response ("
- << response_renderer.getLength()
- << " bytes):\n" << message.toText() << endl;
+ << renderer.getLength()
+ << " bytes):\n" << message->toText() << endl;
}
return (true);
}
bool
-AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, Message& message,
- MessageRenderer& response_renderer)
+AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, MessagePtr message,
+ OutputBufferPtr buffer)
{
// Increment query counter.
incCounter(io_message.getSocket().getProtocol());
@@ -373,8 +499,7 @@
if (verbose_mode_) {
cerr << "[b10-auth] AXFR query over UDP isn't allowed" << endl;
}
- makeErrorMessage(message, response_renderer, Rcode::FORMERR(),
- verbose_mode_);
+ makeErrorMessage(message, buffer, Rcode::FORMERR(), verbose_mode_);
return (true);
}
@@ -401,8 +526,7 @@
cerr << "[b10-auth] Error in handling XFR request: " << err.what()
<< endl;
}
- makeErrorMessage(message, response_renderer, Rcode::SERVFAIL(),
- verbose_mode_);
+ makeErrorMessage(message, buffer, Rcode::SERVFAIL(), verbose_mode_);
return (true);
}
@@ -410,28 +534,26 @@
}
bool
-AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
- MessageRenderer& response_renderer)
+AuthSrvImpl::processNotify(const IOMessage& io_message, MessagePtr message,
+ OutputBufferPtr buffer)
{
// The incoming notify must contain exactly one question for SOA of the
// zone name.
- if (message.getRRCount(Message::SECTION_QUESTION) != 1) {
+ if (message->getRRCount(Message::SECTION_QUESTION) != 1) {
if (verbose_mode_) {
cerr << "[b10-auth] invalid number of questions in notify: "
- << message.getRRCount(Message::SECTION_QUESTION) << endl;
- }
- makeErrorMessage(message, response_renderer, Rcode::FORMERR(),
- verbose_mode_);
+ << message->getRRCount(Message::SECTION_QUESTION) << endl;
+ }
+ makeErrorMessage(message, buffer, Rcode::FORMERR(), verbose_mode_);
return (true);
}
- ConstQuestionPtr question = *message.beginQuestion();
+ ConstQuestionPtr question = *message->beginQuestion();
if (question->getType() != RRType::SOA()) {
if (verbose_mode_) {
cerr << "[b10-auth] invalid question RR type in notify: "
<< question->getType() << endl;
}
- makeErrorMessage(message, response_renderer, Rcode::FORMERR(),
- verbose_mode_);
+ makeErrorMessage(message, buffer, Rcode::FORMERR(), verbose_mode_);
return (true);
}
@@ -489,15 +611,17 @@
return (false);
}
- message.makeResponse();
- message.setHeaderFlag(Message::HEADERFLAG_AA);
- message.setRcode(Rcode::NOERROR());
- message.toWire(response_renderer);
+ message->makeResponse();
+ message->setHeaderFlag(Message::HEADERFLAG_AA);
+ message->setRcode(Rcode::NOERROR());
+
+ MessageRenderer renderer(*buffer);
+ message->toWire(renderer);
return (true);
}
void
-AuthSrvImpl::incCounter(int protocol) {
+AuthSrvImpl::incCounter(const int protocol) {
// Increment query counter.
if (protocol == IPPROTO_UDP) {
counters_.inc(AuthCounters::COUNTER_UDP_QUERY);
@@ -567,6 +691,9 @@
try {
// the ModuleCCSession has already checked if we have
// the correct ElementPtr type as specified in our .spec file
+ if (new_config) {
+ configureAuthServer(*this, new_config);
+ }
return (impl_->setDbFile(new_config));
} catch (const isc::Exception& error) {
if (impl_->verbose_mode_) {
Modified: branches/trac347/src/bin/auth/auth_srv.h
==============================================================================
--- branches/trac347/src/bin/auth/auth_srv.h (original)
+++ branches/trac347/src/bin/auth/auth_srv.h Mon Dec 27 11:50:02 2010
@@ -19,26 +19,25 @@
#include <string>
+// For MemoryDataSrcPtr below. This should be a temporary definition until
+// we reorganize the data source framework.
+#include <boost/shared_ptr.hpp>
+
#include <cc/data.h>
#include <config/ccsession.h>
+#include <asiolink/asiolink.h>
#include <auth/statistics.h>
namespace isc {
-namespace dns {
-class InputBuffer;
-class Message;
-class MessageRenderer;
+namespace datasrc {
+class MemoryDataSrc;
}
-
namespace xfr {
class AbstractXfroutClient;
-};
}
-
-namespace asio_link {
-class IOMessage;
}
+
/// \brief The implementation class for the \c AuthSrv class using the pimpl
/// idiom.
@@ -87,13 +86,28 @@
isc::xfr::AbstractXfroutClient& xfrout_client);
~AuthSrv();
//@}
- /// \return \c true if the \a message contains a response to be returned;
- /// otherwise \c false.
+
+ /// \brief Process an incoming DNS message, then signal 'server' to resume
+ ///
+ /// A DNS query (or other message) has been received by a \c DNSServer
+ /// object. Find an answer, then post the \c DNSServer object on the
+ /// I/O service queue and return. When the server resumes, it can
+ /// send the reply.
+ ///
+ /// \param io_message The raw message received
+ /// \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
- bool processMessage(const asio_link::IOMessage& io_message,
- isc::dns::Message& message,
- isc::dns::MessageRenderer& response_renderer);
+ void processMessage(const asiolink::IOMessage& io_message,
+ isc::dns::MessagePtr message,
+ isc::dns::OutputBufferPtr buffer,
+ asiolink::DNSServer* server);
+
+ /// \brief Set verbose flag
+ ///
+ /// \param on The new value of the verbose flag
/// \brief Enable or disable verbose logging.
///
@@ -108,6 +122,8 @@
/// This method never throws an exception.
///
/// \return \c true if verbose logging is enabled; otherwise \c false.
+
+ /// \brief Get the current value of the verbose flag
bool getVerbose() const;
/// \brief Updates the data source for the \c AuthSrv object.
@@ -119,9 +135,11 @@
/// If there is a data source installed, it will be replaced with the
/// new one.
///
- /// In the current implementation, the SQLite data source is assumed.
- /// The \c config parameter will simply be passed to the initialization
- /// routine of the \c Sqlite3DataSrc class.
+ /// In the current implementation, the SQLite data source and MemoryDataSrc
+ /// are assumed.
+ /// We can enable memory data source and get the path of SQLite database by
+ /// the \c config parameter. If we disabled memory data source, the SQLite
+ /// data source will be used.
///
/// On success this method returns a data \c Element (in the form of a
/// pointer like object) indicating the successful result,
@@ -166,6 +184,21 @@
/// \param config_session A pointer to \c ModuleCCSession object to receive
/// control commands and configuration updates.
void setConfigSession(isc::config::ModuleCCSession* config_session);
+
+ /// \brief Assign an ASIO IO Service queue to this Recursor object
+ void setIOService(asiolink::IOService& ios) { io_service_ = &ios; }
+
+ /// \brief Return this object's ASIO IO Service queue
+ asiolink::IOService& getIOService() const { return (*io_service_); }
+
+ /// \brief Return pointer to the DNS Lookup callback function
+ asiolink::DNSLookup* getDNSLookupProvider() const { return (dns_lookup_); }
+
+ /// \brief Return pointer to the DNS Answer callback function
+ asiolink::DNSAnswer* getDNSAnswerProvider() const { return (dns_answer_); }
+
+ /// \brief Return pointer to the Checkin callback function
+ asiolink::SimpleCallback* getCheckinProvider() const { return (checkin_); }
/// \brief Set or update the size (number of slots) of hot spot cache.
///
@@ -205,6 +238,53 @@
///
void setXfrinSession(isc::cc::AbstractSession* xfrin_session);
+ /// A shared pointer type for \c MemoryDataSrc.
+ ///
+ /// This is defined inside the \c AuthSrv class as it's supposed to be
+ /// a short term interface until we integrate the in-memory and other
+ /// data source frameworks.
+ typedef boost::shared_ptr<isc::datasrc::MemoryDataSrc> MemoryDataSrcPtr;
+
+ /// An immutable shared pointer type for \c MemoryDataSrc.
+ typedef boost::shared_ptr<const isc::datasrc::MemoryDataSrc>
+ ConstMemoryDataSrcPtr;
+
+ /// Returns the in-memory data source configured for the \c AuthSrv,
+ /// if any.
+ ///
+ /// The in-memory data source is configured per RR class. However,
+ /// the data source may not be available for all RR classes.
+ /// If it is not available for the specified RR class, an exception of
+ /// class \c InvalidParameter will be thrown.
+ /// This method never throws an exception otherwise.
+ ///
+ /// Even for supported RR classes, the in-memory data source is not
+ /// configured by default. In that case a NULL (shared) pointer will
+ /// be returned.
+ ///
+ /// \param rrclass The RR class of the requested in-memory data source.
+ /// \return A pointer to the in-memory data source, if configured;
+ /// otherwise NULL.
+ ConstMemoryDataSrcPtr
+ getMemoryDataSrc(const isc::dns::RRClass& rrclass) const;
+
+ /// Sets or replaces the in-memory data source of the specified RR class.
+ ///
+ /// As noted in \c getMemoryDataSrc(), some RR classes may not be
+ /// supported, in which case an exception of class \c InvalidParameter
+ /// will be thrown.
+ /// This method never throws an exception otherwise.
+ ///
+ /// If there is already an in memory data source configured, it will be
+ /// replaced with the newly specified one.
+ /// \c memory_datasrc can be NULL, in which case it will (re)disable the
+ /// in-memory data source.
+ ///
+ /// \param rrclass The RR class of the in-memory data source to be set.
+ /// \param memory_datasrc A (shared) pointer to \c MemoryDataSrc to be set.
+ 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
@@ -222,7 +302,6 @@
/// 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.
@@ -232,7 +311,6 @@
///
/// \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.
@@ -248,10 +326,14 @@
/// \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_;
+ asiolink::SimpleCallback* checkin_;
+ asiolink::DNSLookup* dns_lookup_;
+ asiolink::DNSAnswer* dns_answer_;
};
#endif // __AUTH_SRV_H
Modified: branches/trac347/src/bin/auth/benchmarks/Makefile.am
==============================================================================
--- branches/trac347/src/bin/auth/benchmarks/Makefile.am (original)
+++ branches/trac347/src/bin/auth/benchmarks/Makefile.am Mon Dec 27 11:50:02 2010
@@ -8,7 +8,9 @@
noinst_PROGRAMS = query_bench
query_bench_SOURCES = query_bench.cc
+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
@@ -18,5 +20,5 @@
query_bench_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
query_bench_LDADD += $(top_builddir)/src/lib/cc/libcc.la
query_bench_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
-query_bench_LDADD += $(top_builddir)/src/bin/auth/libasio_link.a
+query_bench_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
query_bench_LDADD += $(SQLITE_LIBS)
Modified: branches/trac347/src/bin/auth/benchmarks/query_bench.cc
==============================================================================
--- branches/trac347/src/bin/auth/benchmarks/query_bench.cc (original)
+++ branches/trac347/src/bin/auth/benchmarks/query_bench.cc Mon Dec 27 11:50:02 2010
@@ -26,7 +26,6 @@
#include <dns/buffer.h>
#include <dns/message.h>
-#include <dns/messagerenderer.h>
#include <dns/name.h>
#include <dns/question.h>
#include <dns/rrclass.h>
@@ -34,19 +33,32 @@
#include <xfr/xfrout_client.h>
#include <auth/auth_srv.h>
-#include <auth/asio_link.h>
+#include <auth/query.h>
+
+#include <asiolink/asiolink.h>
using namespace std;
using namespace isc;
using namespace isc::data;
+using namespace isc::auth;
using namespace isc::dns;
using namespace isc::xfr;
using namespace isc::bench;
-using namespace asio_link;
+using namespace asiolink;
namespace {
// Commonly used constant:
XfroutClient xfrout_client("dummy_path"); // path doesn't matter
+
+// 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 DNSServer* clone() {
+ return new DummyServer(*this);
+ }
+};
class QueryBenchMark {
private:
@@ -56,12 +68,12 @@
typedef boost::shared_ptr<const IOEndpoint> IOEndpointPtr;
public:
QueryBenchMark(const int cache_slots, const char* const datasrc_file,
- const BenchQueries& queries, Message& query_message,
- MessageRenderer& renderer) :
+ const BenchQueries& queries, MessagePtr query_message,
+ OutputBufferPtr buffer) :
server_(new AuthSrv(cache_slots >= 0 ? true : false, xfrout_client)),
queries_(queries),
query_message_(query_message),
- renderer_(renderer),
+ buffer_(buffer),
dummy_socket(IOSocket::getDummyUDPSocket()),
dummy_endpoint(IOEndpointPtr(IOEndpoint::create(IPPROTO_UDP,
IOAddress("192.0.2.1"),
@@ -76,12 +88,13 @@
unsigned int run() {
BenchQueries::const_iterator query;
const BenchQueries::const_iterator query_end = queries_.end();
+ DummyServer server;
for (query = queries_.begin(); query != query_end; ++query) {
IOMessage io_message(&(*query)[0], (*query).size(), dummy_socket,
*dummy_endpoint);
- query_message_.clear(Message::PARSE);
- renderer_.clear();
- server_->processMessage(io_message, query_message_, renderer_);
+ query_message_->clear(Message::PARSE);
+ server_->processMessage(io_message, query_message_, buffer_,
+ &server);
}
return (queries_.size());
@@ -89,11 +102,12 @@
private:
AuthSrvPtr server_;
const BenchQueries& queries_;
- Message& query_message_;
- MessageRenderer& renderer_;
+ MessagePtr query_message_;
+ OutputBufferPtr buffer_;
IOSocket& dummy_socket;
IOEndpointPtr dummy_endpoint;
};
+
}
namespace isc {
@@ -143,9 +157,8 @@
BenchQueries queries;
loadQueryData(query_data_file, queries, RRClass::IN());
- OutputBuffer buffer(4096);
- MessageRenderer renderer(buffer);
- Message message(Message::PARSE);
+ OutputBufferPtr buffer(new OutputBuffer(4096));
+ MessagePtr message(new Message(Message::PARSE));
cout << "Parameters:" << endl;
cout << " Iterations: " << iteration << endl;
@@ -157,24 +170,24 @@
<< endl;
BenchMark<QueryBenchMark>(iteration,
QueryBenchMark(0, datasrc_file, queries, message,
- renderer));
+ buffer));
cout << "Benchmark enabling Hot Spot Cache with 10*#queries slots "
<< endl;
BenchMark<QueryBenchMark>(iteration,
QueryBenchMark(10 * queries.size(), datasrc_file,
- queries, message, renderer));
+ queries, message, buffer));
cout << "Benchmark enabling Hot Spot Cache with #queries/2 slots "
<< endl;
BenchMark<QueryBenchMark>(iteration,
QueryBenchMark(queries.size() / 2, datasrc_file,
- queries, message, renderer));
+ queries, message, buffer));
cout << "Benchmark disabling Hot Spot Cache" << endl;
BenchMark<QueryBenchMark>(iteration,
QueryBenchMark(-1, datasrc_file, queries,
- message, renderer));
+ message, buffer));
return (0);
}
Modified: branches/trac347/src/bin/auth/change_user.cc
==============================================================================
--- branches/trac347/src/bin/auth/change_user.cc (original)
+++ branches/trac347/src/bin/auth/change_user.cc Mon Dec 27 11:50:02 2010
@@ -26,6 +26,7 @@
#include <auth/common.h>
using namespace boost;
+using namespace std;
void
changeUser(const char* const username) {
@@ -42,14 +43,14 @@
}
}
if (runas_pw == NULL) {
- isc_throw(FatalError, "Unknown user name or UID:" << username);
+ throw FatalError("Unknown user name or UID:" + string(username));
}
if (setgid(runas_pw->pw_gid) < 0) {
- isc_throw(FatalError, "setgid() failed: " << strerror(errno));
+ throw FatalError("setgid() failed: " + string(strerror(errno)));
}
if (setuid(runas_pw->pw_uid) < 0) {
- isc_throw(FatalError, "setuid() failed: " << strerror(errno));
+ throw FatalError("setuid() failed: " + string(strerror(errno)));
}
}
Modified: branches/trac347/src/bin/auth/common.h
==============================================================================
--- branches/trac347/src/bin/auth/common.h (original)
+++ branches/trac347/src/bin/auth/common.h Mon Dec 27 11:50:02 2010
@@ -17,12 +17,18 @@
#ifndef __COMMON_H
#define __COMMON_H 1
-#include <exceptions/exceptions.h>
+#include <stdexcept>
+#include <string>
-class FatalError : public isc::Exception {
+/// An exception class that is thrown in an unrecoverable error condition.
+///
+/// This exception should not be caught except at the highest level of
+/// the application only for terminating the program gracefully, and so
+/// it cannot be a derived class of \c isc::Exception.
+class FatalError : public std::runtime_error {
public:
- FatalError(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
+ FatalError(const std::string& what) : std::runtime_error(what)
+ {}
};
#endif // __COMMON_H
Modified: branches/trac347/src/bin/auth/main.cc
==============================================================================
--- branches/trac347/src/bin/auth/main.cc (original)
+++ branches/trac347/src/bin/auth/main.cc Mon Dec 27 11:50:02 2010
@@ -41,9 +41,10 @@
#include <auth/spec_config.h>
#include <auth/common.h>
+#include <auth/config.h>
#include <auth/change_user.h>
#include <auth/auth_srv.h>
-#include <auth/asio_link.h>
+#include <asiolink/asiolink.h>
using namespace std;
using namespace isc::data;
@@ -51,13 +52,15 @@
using namespace isc::config;
using namespace isc::dns;
using namespace isc::xfr;
+using namespace asiolink;
namespace {
-bool verbose_mode = false;
-
-const string PROGRAM = "Auth";
-const char* DNSPORT = "5300";
+static bool verbose_mode = false;
+
+// Default port current 5300 for testing purposes
+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.
@@ -66,9 +69,9 @@
/* need global var for config/command handlers.
* todo: turn this around, and put handlers in the authserver
* class itself? */
-AuthSrv *auth_server;
-
-asio_link::IOService* io_service;
+static AuthSrv *auth_server;
+
+static IOService io_service;
ConstElementPtr
my_config_handler(ConstElementPtr new_config) {
@@ -84,7 +87,7 @@
/* let's add that message to our answer as well */
answer = createAnswer(0, args);
} else if (command == "shutdown") {
- io_service->stop();
+ io_service.stop();
} else if (command == "sendstats") {
if (verbose_mode) {
cerr << "[b10-auth] command 'sendstats' received" << endl;
@@ -92,13 +95,22 @@
assert(auth_server != NULL);
auth_server->submitStatistics();
}
-
+
return (answer);
}
void
usage() {
- cerr << "Usage: b10-auth [-a address] [-p port] [-4|-6] [-nv]" << endl;
+ cerr << "Usage: b10-auth [-a address] [-p port] [-u user] [-4|-6] [-nv]"
+ << endl;
+ cerr << "\t-a: specify the address to listen on (default: all) " << endl;
+ cerr << "\t-p: specify the port to listen on (default: " << DNSPORT << ")"
+ << endl;
+ cerr << "\t-4: listen on all IPv4 addresses (incompatible with -a)" << endl;
+ cerr << "\t-6: listen on all IPv6 addresses (incompatible with -a)" << endl;
+ cerr << "\t-n: do not cache answers in memory" << endl;
+ cerr << "\t-u: change process UID to the specified user" << endl;
+ cerr << "\t-v: verbose output" << endl;
exit(1);
}
@@ -156,12 +168,14 @@
}
if (!use_ipv4 && !use_ipv6) {
- cerr << "[b10-auth] Error: -4 and -6 can't coexist" << endl;
+ cerr << "[b10-auth] Error: Cannot specify both -4 and -6 "
+ << "at the same time" << endl;
usage();
}
if ((!use_ipv4 || !use_ipv6) && address != NULL) {
- cerr << "[b10-auth] Error: -4|-6 and -a can't coexist" << endl;
+ cerr << "[b10-auth] Error: Cannot specify -4 or -6 "
+ << "at the same time as -a" << endl;
usage();
}
@@ -171,7 +185,7 @@
Session* cc_session = NULL;
Session* xfrin_session = NULL;
Session* statistics_session = NULL;
- asio_link::IntervalTimer* itimer = 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;
@@ -195,6 +209,11 @@
auth_server->setVerbose(verbose_mode);
cout << "[b10-auth] Server created." << endl;
+ SimpleCallback* checkin = auth_server->getCheckinProvider();
+ DNSLookup* lookup = auth_server->getDNSLookupProvider();
+ DNSAnswer* answer = auth_server->getDNSAnswerProvider();
+
+ DNSService* dns_service;
if (address != NULL) {
// XXX: we can only specify at most one explicit address.
// This also means the server cannot run in the dual address
@@ -202,15 +221,17 @@
// We don't bother to fix this problem, however. The -a option
// is a short term workaround until we support dynamic listening
// port allocation.
- io_service = new asio_link::IOService(auth_server, *port,
- *address);
+ dns_service = new DNSService(io_service, *port, *address,
+ checkin, lookup, answer);
} else {
- io_service = new asio_link::IOService(auth_server, *port,
- use_ipv4, use_ipv6);
- }
+ dns_service = new DNSService(io_service, *port, use_ipv4,
+ use_ipv6, checkin, lookup,
+ answer);
+ }
+ auth_server->setIOService(io_service);
cout << "[b10-auth] IOService created." << endl;
- cc_session = new Session(io_service->get_io_service());
+ cc_session = new Session(io_service.get_io_service());
cout << "[b10-auth] Configuration session channel created." << endl;
config_session = new ModuleCCSession(specfile, *cc_session,
@@ -222,30 +243,36 @@
changeUser(uid);
}
- xfrin_session = new Session(io_service->get_io_service());
+ xfrin_session = new Session(io_service.get_io_service());
cout << "[b10-auth] Xfrin session channel created." << endl;
xfrin_session->establish(NULL);
xfrin_session_established = true;
cout << "[b10-auth] Xfrin session channel established." << endl;
- statistics_session = new Session(io_service->get_io_service());
+ 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 asio_link we have to create
+ // 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 asio_link independent
+ // 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
+ // handle the traditional "database_file" setup by directly calling
+ // updateConfig().
auth_server->setConfigSession(config_session);
+ configureAuthServer(*auth_server, config_session->getFullConfig());
auth_server->updateConfig(ElementPtr());
// create interval timer instance
- itimer = new asio_link::IntervalTimer(*io_service);
+ itimer = new IntervalTimer(io_service);
// set up interval timer
// register function to send statistics with interval
itimer->setupTimer(boost::bind(statisticsTimerCallback, auth_server),
@@ -253,9 +280,11 @@
cout << "[b10-auth] Interval timer to send statistics set." << endl;
cout << "[b10-auth] Server started." << endl;
- io_service->run();
+ io_service.run();
+
+ delete dns_service;
} catch (const std::exception& ex) {
- cerr << "[b10-auth] Initialization failed: " << ex.what() << endl;
+ cerr << "[b10-auth] Server failed: " << ex.what() << endl;
ret = 1;
}
@@ -272,7 +301,6 @@
delete xfrin_session;
delete config_session;
delete cc_session;
- delete io_service;
delete auth_server;
return (ret);
Modified: branches/trac347/src/bin/auth/tests/Makefile.am
==============================================================================
--- branches/trac347/src/bin/auth/tests/Makefile.am (original)
+++ branches/trac347/src/bin/auth/tests/Makefile.am Mon Dec 27 11:50:02 2010
@@ -1,11 +1,9 @@
-SUBDIRS = testdata .
-
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/bin
AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
AM_CPPFLAGS += $(BOOST_INCLUDES)
-AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(srcdir)/testdata\"
-AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/bin/auth/tests/testdata\"
+AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(top_srcdir)/src/lib/testutils/testdata\"
+AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/testutils/testdata\"
AM_CXXFLAGS = $(B10_CXXFLAGS)
@@ -21,11 +19,14 @@
run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h
run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
run_unittests_SOURCES += ../auth_srv.h ../auth_srv.cc
+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 += asio_link_unittest.cc
run_unittests_SOURCES += statistics_unittest.cc
run_unittests_SOURCES += run_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
@@ -34,10 +35,10 @@
run_unittests_LDADD += $(SQLITE_LIBS)
run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/libdatasrc.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
-run_unittests_LDADD += $(top_builddir)/src/bin/auth/libasio_link.a
run_unittests_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
endif
Modified: branches/trac347/src/bin/auth/tests/auth_srv_unittest.cc
==============================================================================
--- branches/trac347/src/bin/auth/tests/auth_srv_unittest.cc (original)
+++ branches/trac347/src/bin/auth/tests/auth_srv_unittest.cc Mon Dec 27 11:50:02 2010
@@ -15,36 +15,17 @@
// $Id$
#include <config.h>
-
-#include <gtest/gtest.h>
-
-#include <dns/buffer.h>
-#include <dns/name.h>
-#include <dns/message.h>
-#include <dns/messagerenderer.h>
-#include <dns/opcode.h>
-#include <dns/rcode.h>
-#include <dns/rrclass.h>
-#include <dns/rrtype.h>
-
-#include <cc/data.h>
-#include <cc/session.h>
-
-#include <xfr/xfrout_client.h>
-
+#include <datasrc/memory_datasrc.h>
#include <auth/auth_srv.h>
-#include <auth/asio_link.h>
+#include <testutils/srv_unittest.h>
#include <auth/statistics.h>
-#include <dns/tests/unittest_util.h>
-
-using isc::UnitTestUtil;
-using namespace std;
using namespace isc::cc;
using namespace isc::dns;
using namespace isc::data;
using namespace isc::xfr;
-using namespace asio_link;
+using namespace asiolink;
+using isc::UnitTestUtil;
namespace {
const char* const CONFIG_TESTDB =
@@ -53,447 +34,86 @@
// the sqlite3 test).
const char* const BADCONFIG_TESTDB =
"{ \"database_file\": \"" TEST_DATA_DIR "/nodir/notexist\"}";
-const char* const DEFAULT_REMOTE_ADDRESS = "192.0.2.1";
-
-class AuthSrvTest : public ::testing::Test {
-private:
- class MockXfroutClient : public AbstractXfroutClient {
- public:
- MockXfroutClient() :
- is_connected_(false), connect_ok_(true), send_ok_(true),
- disconnect_ok_(true)
- {}
- virtual void connect();
- virtual void disconnect();
- virtual int sendXfroutRequestInfo(int tcp_sock, const void* msg_data,
- uint16_t msg_len);
- bool isConnected() const { return (is_connected_); }
- void disableConnect() { connect_ok_ = false; }
- void disableDisconnect() { disconnect_ok_ = false; }
- void enableDisconnect() { disconnect_ok_ = true; }
- void disableSend() { send_ok_ = false; }
- private:
- bool is_connected_;
- bool connect_ok_;
- bool send_ok_;
- bool disconnect_ok_;
- };
-
- class MockSession : public AbstractSession {
- public:
- MockSession() :
- // by default we return a simple "success" message.
- msg_(Element::fromJSON("{\"result\": [0, \"SUCCESS\"]}")),
- send_ok_(true), receive_ok_(true)
- {}
- virtual void establish(const char* socket_file);
- virtual void disconnect();
- virtual int group_sendmsg(ConstElementPtr msg, string group,
- string instance, string to);
- virtual bool group_recvmsg(ConstElementPtr& envelope,
- ConstElementPtr& msg,
- bool nonblock, int seq);
- virtual void subscribe(string group, string instance);
- virtual void unsubscribe(string group, string instance);
- virtual void startRead(boost::function<void()> read_callback);
- virtual int reply(ConstElementPtr envelope, ConstElementPtr newmsg);
- virtual bool hasQueuedMsgs() const;
- virtual void setTimeout(size_t) {}
- virtual size_t getTimeout() const { return 0; };
-
- void setMessage(ConstElementPtr msg) { msg_ = msg; }
- void disableSend() { send_ok_ = false; }
- void disableReceive() { receive_ok_ = false; }
-
- ConstElementPtr sent_msg;
- string msg_destination;
- private:
- ConstElementPtr msg_;
- bool send_ok_;
- bool receive_ok_;
- };
-
+
+class AuthSrvTest : public SrvTestBase {
protected:
- AuthSrvTest() : server(true, xfrout),
- request_message(Message::RENDER),
- parse_message(Message::PARSE), default_qid(0x1035),
- opcode(Opcode::QUERY()), qname("www.example.com"),
- qclass(RRClass::IN()), qtype(RRType::A()),
- io_message(NULL), endpoint(NULL), request_obuffer(0),
- request_renderer(request_obuffer),
- response_obuffer(0), response_renderer(response_obuffer)
- {
+ AuthSrvTest() : server(true, xfrout), rrclass(RRClass::IN()) {
server.setXfrinSession(¬ify_session);
server.setStatisticsSession(&statistics_session);
}
- ~AuthSrvTest() {
- delete io_message;
- delete endpoint;
- }
- MockSession notify_session;
MockSession statistics_session;
MockXfroutClient xfrout;
AuthSrv server;
- Message request_message;
- Message parse_message;
- const qid_t default_qid;
- const Opcode opcode;
- const Name qname;
- const RRClass qclass;
- const RRType qtype;
- IOMessage* io_message;
- const IOEndpoint* endpoint;
- OutputBuffer request_obuffer;
- MessageRenderer request_renderer;
- OutputBuffer response_obuffer;
- MessageRenderer response_renderer;
- vector<uint8_t> data;
-
- void createDataFromFile(const char* const datafile, int protocol);
- void createRequestMessage(const Opcode& opcode, const Name& request_name,
- const RRClass& rrclass, const RRType& rrtype);
- void createRequestPacket(const Opcode& opcode, const Name& request_name,
- const RRClass& rrclass, const RRType& rrtype,
- int protocol);
- void createRequestPacket(int protocol);
+ const RRClass rrclass;
};
-
-void
-AuthSrvTest::MockSession::establish(const char*) {}
-
-void
-AuthSrvTest::MockSession::disconnect() {}
-
-void
-AuthSrvTest::MockSession::subscribe(string, string)
-{}
-
-void
-AuthSrvTest::MockSession::unsubscribe(string, string)
-{}
-
-void
-AuthSrvTest::MockSession::startRead(boost::function<void()>)
-{}
-
-int
-AuthSrvTest::MockSession::reply(ConstElementPtr, ConstElementPtr) {
- return (-1);
-}
-
-bool
-AuthSrvTest::MockSession::hasQueuedMsgs() const {
- return (false);
-}
-
-int
-AuthSrvTest::MockSession::group_sendmsg(ConstElementPtr msg, string group,
- string, string)
-{
- if (!send_ok_) {
- isc_throw(XfroutError, "mock session send is disabled for test");
- }
-
- sent_msg = msg;
- msg_destination = group;
- return (0);
-}
-
-bool
-AuthSrvTest::MockSession::group_recvmsg(ConstElementPtr&,
- ConstElementPtr& msg, bool, int)
-{
- if (!receive_ok_) {
- isc_throw(XfroutError, "mock session receive is disabled for test");
- }
-
- msg = msg_;
- return (true);
-}
-
-void
-AuthSrvTest::MockXfroutClient::connect() {
- if (!connect_ok_) {
- isc_throw(XfroutError, "xfrout connection disabled for test");
- }
- is_connected_ = true;
-}
-
-void
-AuthSrvTest::MockXfroutClient::disconnect() {
- if (!disconnect_ok_) {
- isc_throw(XfroutError,
- "closing xfrout connection is disabled for test");
- }
- is_connected_ = false;
-}
-
-int
-AuthSrvTest::MockXfroutClient::sendXfroutRequestInfo(const int,
- const void*,
- const uint16_t)
-{
- if (!send_ok_) {
- isc_throw(XfroutError, "xfrout connection send is disabled for test");
- }
- return (0);
-}
-
-
-// These are flags to indicate whether the corresponding flag bit of the
-// DNS header is to be set in the test cases. (Note that the flag values
-// is irrelevant to their wire-format values)
-const unsigned int QR_FLAG = 0x1;
-const unsigned int AA_FLAG = 0x2;
-const unsigned int TC_FLAG = 0x4;
-const unsigned int RD_FLAG = 0x8;
-const unsigned int RA_FLAG = 0x10;
-const unsigned int AD_FLAG = 0x20;
-const unsigned int CD_FLAG = 0x40;
-
-void
-AuthSrvTest::createDataFromFile(const char* const datafile,
- const int protocol = IPPROTO_UDP)
-{
- delete io_message;
- data.clear();
-
- delete endpoint;
- endpoint = IOEndpoint::create(protocol,
- IOAddress(DEFAULT_REMOTE_ADDRESS), 5300);
- UnitTestUtil::readWireData(datafile, data);
- io_message = new IOMessage(&data[0], data.size(),
- protocol == IPPROTO_UDP ?
- IOSocket::getDummyUDPSocket() :
- IOSocket::getDummyTCPSocket(), *endpoint);
-}
-
-void
-AuthSrvTest::createRequestMessage(const Opcode& opcode,
- const Name& request_name,
- const RRClass& rrclass,
- const RRType& rrtype)
-{
- request_message.clear(Message::RENDER);
- request_message.setOpcode(opcode);
- request_message.setRcode(Rcode::NOERROR());
- request_message.setQid(default_qid);
- request_message.addQuestion(Question(request_name, rrclass, rrtype));
-}
-
-void
-AuthSrvTest::createRequestPacket(const Opcode& opcode,
- const Name& request_name,
- const RRClass& rrclass, const RRType& rrtype,
- const int protocol = IPPROTO_UDP)
-{
- createRequestMessage(opcode, request_name, rrclass, rrtype);
- createRequestPacket(protocol);
-}
-
-void
-AuthSrvTest::createRequestPacket(const int protocol = IPPROTO_UDP) {
- request_message.toWire(request_renderer);
-
- delete io_message;
- endpoint = IOEndpoint::create(protocol,
- IOAddress(DEFAULT_REMOTE_ADDRESS), 5300);
- io_message = new IOMessage(request_renderer.getData(),
- request_renderer.getLength(),
- protocol == IPPROTO_UDP ?
- IOSocket::getDummyUDPSocket() :
- IOSocket::getDummyTCPSocket(), *endpoint);
-}
-
-void
-headerCheck(const Message& message, const qid_t qid, const Rcode& rcode,
- const uint16_t opcodeval, const unsigned int flags,
- const unsigned int qdcount,
- const unsigned int ancount, const unsigned int nscount,
- const unsigned int arcount)
-{
- EXPECT_EQ(qid, message.getQid());
- EXPECT_EQ(rcode, message.getRcode());
- EXPECT_EQ(opcodeval, message.getOpcode().getCode());
- EXPECT_EQ((flags & QR_FLAG) != 0,
- message.getHeaderFlag(Message::HEADERFLAG_QR));
- EXPECT_EQ((flags & AA_FLAG) != 0,
- message.getHeaderFlag(Message::HEADERFLAG_AA));
- EXPECT_EQ((flags & TC_FLAG) != 0,
- message.getHeaderFlag(Message::HEADERFLAG_TC));
- EXPECT_EQ((flags & RA_FLAG) != 0,
- message.getHeaderFlag(Message::HEADERFLAG_RA));
- EXPECT_EQ((flags & RD_FLAG) != 0,
- message.getHeaderFlag(Message::HEADERFLAG_RD));
- EXPECT_EQ((flags & AD_FLAG) != 0,
- message.getHeaderFlag(Message::HEADERFLAG_AD));
- EXPECT_EQ((flags & CD_FLAG) != 0,
- message.getHeaderFlag(Message::HEADERFLAG_CD));
-
- EXPECT_EQ(qdcount, message.getRRCount(Message::SECTION_QUESTION));
- EXPECT_EQ(ancount, message.getRRCount(Message::SECTION_ANSWER));
- EXPECT_EQ(nscount, message.getRRCount(Message::SECTION_AUTHORITY));
- EXPECT_EQ(arcount, message.getRRCount(Message::SECTION_ADDITIONAL));
-}
// Unsupported requests. Should result in NOTIMP.
TEST_F(AuthSrvTest, unsupportedRequest) {
- for (unsigned int i = 0; i < 16; ++i) {
- // set Opcode to 'i', which iterators over all possible codes except
- // the standard query and notify
- if (i == Opcode::QUERY().getCode() ||
- i == Opcode::NOTIFY().getCode()) {
- continue;
- }
- createDataFromFile("simplequery_fromWire.wire");
- data[2] = ((i << 3) & 0xff);
-
- parse_message.clear(Message::PARSE);
- EXPECT_TRUE(server.processMessage(*io_message, parse_message,
- response_renderer));
- headerCheck(parse_message, default_qid, Rcode::NOTIMP(), i, QR_FLAG,
- 0, 0, 0, 0);
- }
+ UNSUPPORTED_REQUEST_TEST;
}
// Simple API check
TEST_F(AuthSrvTest, verbose) {
- EXPECT_FALSE(server.getVerbose());
- server.setVerbose(true);
- EXPECT_TRUE(server.getVerbose());
- server.setVerbose(false);
- EXPECT_FALSE(server.getVerbose());
+ VERBOSE_TEST;
}
// Multiple questions. Should result in FORMERR.
TEST_F(AuthSrvTest, multiQuestion) {
- createDataFromFile("multiquestion_fromWire.wire");
- EXPECT_TRUE(server.processMessage(*io_message, parse_message,
- response_renderer));
- headerCheck(parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
- QR_FLAG, 2, 0, 0, 0);
-
- QuestionIterator qit = parse_message.beginQuestion();
- EXPECT_EQ(Name("example.com"), (*qit)->getName());
- EXPECT_EQ(RRClass::IN(), (*qit)->getClass());
- EXPECT_EQ(RRType::A(), (*qit)->getType());
- ++qit;
- EXPECT_EQ(Name("example.com"), (*qit)->getName());
- EXPECT_EQ(RRClass::IN(), (*qit)->getClass());
- EXPECT_EQ(RRType::AAAA(), (*qit)->getType());
- ++qit;
- EXPECT_TRUE(qit == parse_message.endQuestion());
+ MULTI_QUESTION_TEST;
}
// Incoming data doesn't even contain the complete header. Must be silently
// dropped.
TEST_F(AuthSrvTest, shortMessage) {
- createDataFromFile("shortmessage_fromWire");
- EXPECT_FALSE(server.processMessage(*io_message, parse_message,
- response_renderer));
+ SHORT_MESSAGE_TEST;
}
// Response messages. Must be silently dropped, whether it's a valid response
// or malformed or could otherwise cause a protocol error.
TEST_F(AuthSrvTest, response) {
- // A valid (although unusual) response
- createDataFromFile("simpleresponse_fromWire.wire");
- EXPECT_FALSE(server.processMessage(*io_message, parse_message,
- response_renderer));
-
- // A response with a broken question section. must be dropped rather than
- // returning FORMERR.
- createDataFromFile("shortresponse_fromWire");
- EXPECT_FALSE(server.processMessage(*io_message, parse_message,
- response_renderer));
-
- // A response to iquery. must be dropped rather than returning NOTIMP.
- createDataFromFile("iqueryresponse_fromWire.wire");
- EXPECT_FALSE(server.processMessage(*io_message, parse_message,
- response_renderer));
+ RESPONSE_TEST;
}
// Query with a broken question
TEST_F(AuthSrvTest, shortQuestion) {
- createDataFromFile("shortquestion_fromWire");
- EXPECT_TRUE(server.processMessage(*io_message, parse_message,
- response_renderer));
- // Since the query's question is broken, the question section of the
- // response should be empty.
- headerCheck(parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
- QR_FLAG, 0, 0, 0, 0);
+ SHORT_QUESTION_TEST;
}
// Query with a broken answer section
TEST_F(AuthSrvTest, shortAnswer) {
- createDataFromFile("shortanswer_fromWire.wire");
- EXPECT_TRUE(server.processMessage(*io_message, parse_message,
- response_renderer));
-
- // This is a bogus query, but question section is valid. So the response
- // should copy the question section.
- headerCheck(parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
- QR_FLAG, 1, 0, 0, 0);
-
- QuestionIterator qit = parse_message.beginQuestion();
- EXPECT_EQ(Name("example.com"), (*qit)->getName());
- EXPECT_EQ(RRClass::IN(), (*qit)->getClass());
- EXPECT_EQ(RRType::A(), (*qit)->getType());
- ++qit;
- EXPECT_TRUE(qit == parse_message.endQuestion());
+ SHORT_ANSWER_TEST;
}
// Query with unsupported version of EDNS.
TEST_F(AuthSrvTest, ednsBadVers) {
- createDataFromFile("queryBadEDNS_fromWire.wire");
- EXPECT_TRUE(server.processMessage(*io_message, parse_message,
- response_renderer));
-
- // The response must have an EDNS OPT RR in the additional section, but
- // it will be added automatically at the render time.
- // Note that the DNSSEC DO bit is cleared even if this bit in the query
- // is set. This is a limitation of the current implementation.
- headerCheck(parse_message, default_qid, Rcode::BADVERS(), opcode.getCode(),
- QR_FLAG, 1, 0, 0, 1);
- EXPECT_FALSE(parse_message.getEDNS()); // EDNS isn't added at this point
-
- parse_message.clear(Message::PARSE);
- InputBuffer ib(response_renderer.getData(), response_renderer.getLength());
- parse_message.fromWire(ib);
- EXPECT_EQ(Rcode::BADVERS(), parse_message.getRcode());
- EXPECT_TRUE(parse_message.getEDNS());
- EXPECT_FALSE(parse_message.getEDNS()->getDNSSECAwareness());
+ EDNS_BADVERS_TEST;
}
TEST_F(AuthSrvTest, AXFROverUDP) {
- // AXFR over UDP is invalid and should result in FORMERR.
- createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
- RRType::AXFR(), IPPROTO_UDP);
- EXPECT_TRUE(server.processMessage(*io_message, parse_message,
- response_renderer));
- headerCheck(parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
- QR_FLAG, 1, 0, 0, 0);
+ AXFR_OVER_UDP_TEST;
}
TEST_F(AuthSrvTest, AXFRSuccess) {
EXPECT_FALSE(xfrout.isConnected());
- createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
- RRType::AXFR(), IPPROTO_TCP);
+ 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.
- EXPECT_FALSE(server.processMessage(*io_message, parse_message,
- response_renderer));
+ server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ EXPECT_FALSE(dnsserv.hasAnswer());
EXPECT_TRUE(xfrout.isConnected());
}
TEST_F(AuthSrvTest, AXFRConnectFail) {
EXPECT_FALSE(xfrout.isConnected()); // check prerequisite
xfrout.disableConnect();
- createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
- RRType::AXFR(), IPPROTO_TCP);
- EXPECT_TRUE(server.processMessage(*io_message, parse_message,
- response_renderer));
- headerCheck(parse_message, default_qid, Rcode::SERVFAIL(),
+ UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
+ Name("example.com"), RRClass::IN(), RRType::AXFR());
+ createRequestPacket(request_message, IPPROTO_TCP);
+ server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
EXPECT_FALSE(xfrout.isConnected());
}
@@ -501,19 +121,21 @@
TEST_F(AuthSrvTest, AXFRSendFail) {
// first send a valid query, making the connection with the xfr process
// open.
- createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
- RRType::AXFR(), IPPROTO_TCP);
- server.processMessage(*io_message, parse_message, response_renderer);
+ UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
+ Name("example.com"), RRClass::IN(), RRType::AXFR());
+ createRequestPacket(request_message, IPPROTO_TCP);
+ server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
EXPECT_TRUE(xfrout.isConnected());
xfrout.disableSend();
- parse_message.clear(Message::PARSE);
- response_renderer.clear();
- createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
- RRType::AXFR(), IPPROTO_TCP);
- EXPECT_TRUE(server.processMessage(*io_message, parse_message,
- response_renderer));
- headerCheck(parse_message, default_qid, Rcode::SERVFAIL(),
+ parse_message->clear(Message::PARSE);
+ response_obuffer->clear();
+ UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
+ Name("example.com"), RRClass::IN(), RRType::AXFR());
+ createRequestPacket(request_message, IPPROTO_TCP);
+ server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
// The connection should have been closed due to the send failure.
@@ -525,10 +147,11 @@
// should it be thrown.
xfrout.disableSend();
xfrout.disableDisconnect();
- createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
- RRType::AXFR(), IPPROTO_TCP);
+ UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
+ Name("example.com"), RRClass::IN(), RRType::AXFR());
+ createRequestPacket(request_message, IPPROTO_TCP);
EXPECT_THROW(server.processMessage(*io_message, parse_message,
- response_renderer),
+ response_obuffer, &dnsserv),
XfroutError);
EXPECT_TRUE(xfrout.isConnected());
// XXX: we need to re-enable disconnect. otherwise an exception would be
@@ -537,31 +160,31 @@
}
TEST_F(AuthSrvTest, notify) {
- createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
- RRType::SOA());
- request_message.setHeaderFlag(Message::HEADERFLAG_AA);
- createRequestPacket(IPPROTO_UDP);
- EXPECT_TRUE(server.processMessage(*io_message, parse_message,
- response_renderer));
+ UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
+ Name("example.com"), RRClass::IN(), RRType::SOA());
+ request_message.setHeaderFlag(Message::HEADERFLAG_AA);
+ createRequestPacket(request_message, IPPROTO_UDP);
+ server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ EXPECT_TRUE(dnsserv.hasAnswer());
// An internal command message should have been created and sent to an
// external module. Check them.
- EXPECT_EQ("Zonemgr", notify_session.msg_destination);
+ EXPECT_EQ("Zonemgr", notify_session.getMessageDest());
EXPECT_EQ("notify",
- notify_session.sent_msg->get("command")->get(0)->stringValue());
+ notify_session.getSentMessage()->get("command")->get(0)->stringValue());
ConstElementPtr notify_args =
- notify_session.sent_msg->get("command")->get(1);
+ notify_session.getSentMessage()->get("command")->get(1);
EXPECT_EQ("example.com.", notify_args->get("zone_name")->stringValue());
EXPECT_EQ(DEFAULT_REMOTE_ADDRESS,
notify_args->get("master")->stringValue());
EXPECT_EQ("IN", notify_args->get("zone_class")->stringValue());
// On success, the server should return a response to the notify.
- headerCheck(parse_message, default_qid, Rcode::NOERROR(),
+ headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
// The question must be identical to that of the received notify
- ConstQuestionPtr question = *parse_message.beginQuestion();
+ ConstQuestionPtr question = *parse_message->beginQuestion();
EXPECT_EQ(Name("example.com"), question->getName());
EXPECT_EQ(RRClass::IN(), question->getClass());
EXPECT_EQ(RRType::SOA(), question->getType());
@@ -569,17 +192,17 @@
TEST_F(AuthSrvTest, notifyForCHClass) {
// Same as the previous test, but for the CH RRClass.
- createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::CH(),
- RRType::SOA());
- request_message.setHeaderFlag(Message::HEADERFLAG_AA);
- createRequestPacket(IPPROTO_UDP);
- EXPECT_TRUE(server.processMessage(*io_message, parse_message,
- response_renderer));
+ UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
+ Name("example.com"), RRClass::CH(), RRType::SOA());
+ request_message.setHeaderFlag(Message::HEADERFLAG_AA);
+ createRequestPacket(request_message, IPPROTO_UDP);
+ server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ EXPECT_TRUE(dnsserv.hasAnswer());
// Other conditions should be the same, so simply confirm the RR class is
// set correctly.
ConstElementPtr notify_args =
- notify_session.sent_msg->get("command")->get(1);
+ notify_session.getSentMessage()->get("command")->get(1);
EXPECT_EQ("CH", notify_args->get("zone_class")->stringValue());
}
@@ -590,126 +213,127 @@
request_message.setHeaderFlag(Message::HEADERFLAG_AA);
request_message.setQid(default_qid);
request_message.toWire(request_renderer);
- createRequestPacket(IPPROTO_UDP);
- EXPECT_TRUE(server.processMessage(*io_message, parse_message,
- response_renderer));
- headerCheck(parse_message, default_qid, Rcode::FORMERR(),
+ createRequestPacket(request_message, IPPROTO_UDP);
+ server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);
}
TEST_F(AuthSrvTest, notifyMultiQuestions) {
- createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
- RRType::SOA());
+ UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
+ Name("example.com"), RRClass::IN(), RRType::SOA());
// add one more SOA question
request_message.addQuestion(Question(Name("example.com"), RRClass::IN(),
RRType::SOA()));
request_message.setHeaderFlag(Message::HEADERFLAG_AA);
- createRequestPacket(IPPROTO_UDP);
- EXPECT_TRUE(server.processMessage(*io_message, parse_message,
- response_renderer));
- headerCheck(parse_message, default_qid, Rcode::FORMERR(),
+ createRequestPacket(request_message, IPPROTO_UDP);
+ server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
Opcode::NOTIFY().getCode(), QR_FLAG, 2, 0, 0, 0);
}
TEST_F(AuthSrvTest, notifyNonSOAQuestion) {
- createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
- RRType::NS());
- request_message.setHeaderFlag(Message::HEADERFLAG_AA);
- createRequestPacket(IPPROTO_UDP);
- EXPECT_TRUE(server.processMessage(*io_message, parse_message,
- response_renderer));
- headerCheck(parse_message, default_qid, Rcode::FORMERR(),
+ UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
+ Name("example.com"), RRClass::IN(), RRType::NS());
+ request_message.setHeaderFlag(Message::HEADERFLAG_AA);
+ createRequestPacket(request_message, IPPROTO_UDP);
+ server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
Opcode::NOTIFY().getCode(), QR_FLAG, 1, 0, 0, 0);
}
TEST_F(AuthSrvTest, notifyWithoutAA) {
// implicitly leave the AA bit off. our implementation will accept it.
- createRequestPacket(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
- RRType::SOA());
- EXPECT_TRUE(server.processMessage(*io_message, parse_message,
- response_renderer));
- headerCheck(parse_message, default_qid, Rcode::NOERROR(),
+ UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
+ Name("example.com"), RRClass::IN(), RRType::SOA());
+ createRequestPacket(request_message, IPPROTO_UDP);
+ server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
}
TEST_F(AuthSrvTest, notifyWithErrorRcode) {
- createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
- RRType::SOA());
+ UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
+ Name("example.com"), RRClass::IN(), RRType::SOA());
request_message.setHeaderFlag(Message::HEADERFLAG_AA);
request_message.setRcode(Rcode::SERVFAIL());
- createRequestPacket(IPPROTO_UDP);
- EXPECT_TRUE(server.processMessage(*io_message, parse_message,
- response_renderer));
- headerCheck(parse_message, default_qid, Rcode::NOERROR(),
+ createRequestPacket(request_message, IPPROTO_UDP);
+ server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
}
TEST_F(AuthSrvTest, notifyWithoutSession) {
server.setXfrinSession(NULL);
- createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
- RRType::SOA());
- request_message.setHeaderFlag(Message::HEADERFLAG_AA);
- createRequestPacket(IPPROTO_UDP);
+ UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
+ Name("example.com"), RRClass::IN(), RRType::SOA());
+ request_message.setHeaderFlag(Message::HEADERFLAG_AA);
+ createRequestPacket(request_message, IPPROTO_UDP);
// we simply ignore the notify and let it be resent if an internal error
// happens.
- EXPECT_FALSE(server.processMessage(*io_message, parse_message,
- response_renderer));
+ server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ EXPECT_FALSE(dnsserv.hasAnswer());
}
TEST_F(AuthSrvTest, notifySendFail) {
notify_session.disableSend();
- createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
- RRType::SOA());
- request_message.setHeaderFlag(Message::HEADERFLAG_AA);
- createRequestPacket(IPPROTO_UDP);
-
- EXPECT_FALSE(server.processMessage(*io_message, parse_message,
- response_renderer));
+ UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
+ Name("example.com"), RRClass::IN(), RRType::SOA());
+ request_message.setHeaderFlag(Message::HEADERFLAG_AA);
+ createRequestPacket(request_message, IPPROTO_UDP);
+
+ server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ EXPECT_FALSE(dnsserv.hasAnswer());
}
TEST_F(AuthSrvTest, notifyReceiveFail) {
notify_session.disableReceive();
- createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
- RRType::SOA());
- request_message.setHeaderFlag(Message::HEADERFLAG_AA);
- createRequestPacket(IPPROTO_UDP);
- EXPECT_FALSE(server.processMessage(*io_message, parse_message,
- response_renderer));
+ UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
+ Name("example.com"), RRClass::IN(), RRType::SOA());
+ request_message.setHeaderFlag(Message::HEADERFLAG_AA);
+ createRequestPacket(request_message, IPPROTO_UDP);
+ server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ EXPECT_FALSE(dnsserv.hasAnswer());
}
TEST_F(AuthSrvTest, notifyWithBogusSessionMessage) {
notify_session.setMessage(Element::fromJSON("{\"foo\": 1}"));
- createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
- RRType::SOA());
- request_message.setHeaderFlag(Message::HEADERFLAG_AA);
- createRequestPacket(IPPROTO_UDP);
- EXPECT_FALSE(server.processMessage(*io_message, parse_message,
- response_renderer));
+ UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
+ Name("example.com"), RRClass::IN(), RRType::SOA());
+ request_message.setHeaderFlag(Message::HEADERFLAG_AA);
+ createRequestPacket(request_message, IPPROTO_UDP);
+ server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ EXPECT_FALSE(dnsserv.hasAnswer());
}
TEST_F(AuthSrvTest, notifyWithSessionMessageError) {
notify_session.setMessage(
Element::fromJSON("{\"result\": [1, \"FAIL\"]}"));
- createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
- RRType::SOA());
- request_message.setHeaderFlag(Message::HEADERFLAG_AA);
- createRequestPacket(IPPROTO_UDP);
- EXPECT_FALSE(server.processMessage(*io_message, parse_message,
- response_renderer));
+ UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
+ Name("example.com"), RRClass::IN(), RRType::SOA());
+ request_message.setHeaderFlag(Message::HEADERFLAG_AA);
+ createRequestPacket(request_message, IPPROTO_UDP);
+ server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ EXPECT_FALSE(dnsserv.hasAnswer());
}
void
-updateConfig(AuthSrv* server, const char* const dbfile,
+updateConfig(AuthSrv* server, const char* const config_data,
const bool expect_success)
{
ConstElementPtr config_answer =
- server->updateConfig(Element::fromJSON(dbfile));
+ server->updateConfig(Element::fromJSON(config_data));
EXPECT_EQ(Element::map, config_answer->getType());
EXPECT_TRUE(config_answer->contains("result"));
@@ -726,9 +350,9 @@
// response should have the AA flag on, and have an RR in each answer
// and authority section.
createDataFromFile("examplequery_fromWire.wire");
- EXPECT_TRUE(server.processMessage(*io_message, parse_message,
- response_renderer));
- headerCheck(parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
+ server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
QR_FLAG | AA_FLAG, 1, 1, 1, 0);
}
@@ -740,9 +364,9 @@
// in a SERVFAIL response, and the answer and authority sections should
// be empty.
createDataFromFile("badExampleQuery_fromWire.wire");
- EXPECT_TRUE(server.processMessage(*io_message, parse_message,
- response_renderer));
- headerCheck(parse_message, default_qid, Rcode::SERVFAIL(),
+ server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
}
@@ -755,10 +379,48 @@
// The original data source should still exist.
createDataFromFile("examplequery_fromWire.wire");
- EXPECT_TRUE(server.processMessage(*io_message, parse_message,
- response_renderer));
- headerCheck(parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
+ server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
QR_FLAG | AA_FLAG, 1, 1, 1, 0);
+}
+
+TEST_F(AuthSrvTest, updateWithMemoryDataSrc) {
+ // Test configuring memory data source. Detailed test cases are covered
+ // in the configuration tests. We only check the AuthSrv interface here.
+
+ // By default memory data source isn't enabled
+ EXPECT_EQ(AuthSrv::MemoryDataSrcPtr(), server.getMemoryDataSrc(rrclass));
+ updateConfig(&server,
+ "{\"datasources\": [{\"type\": \"memory\"}]}", true);
+ // after successful configuration, we should have one (with empty zoneset).
+ ASSERT_NE(AuthSrv::MemoryDataSrcPtr(), server.getMemoryDataSrc(rrclass));
+ EXPECT_EQ(0, server.getMemoryDataSrc(rrclass)->getZoneCount());
+
+ // The memory data source is empty, should return REFUSED rcode.
+ createDataFromFile("examplequery_fromWire.wire");
+ server.processMessage(*io_message, parse_message, response_obuffer,
+ &dnsserv);
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, Rcode::REFUSED(),
+ opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
+}
+
+TEST_F(AuthSrvTest, chQueryWithMemoryDataSrc) {
+ // Configure memory data source for class IN
+ updateConfig(&server, "{\"datasources\": "
+ "[{\"class\": \"IN\", \"type\": \"memory\"}]}", true);
+
+ // This shouldn't affect the result of class CH query
+ 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);
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
+ opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 1, 0);
}
TEST_F(AuthSrvTest, cacheSlots) {
@@ -775,10 +437,13 @@
TEST_F(AuthSrvTest, queryCounterUDPNormal) {
// The counter should be initialized to 0.
EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_UDP_QUERY));
- createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
- RRType::NS(), IPPROTO_UDP);
- EXPECT_TRUE(server.processMessage(*io_message, parse_message,
- response_renderer));
+ // 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));
}
@@ -787,10 +452,13 @@
TEST_F(AuthSrvTest, queryCounterTCPNormal) {
// The counter should be initialized to 0.
EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
- createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
- RRType::NS(), IPPROTO_TCP);
- EXPECT_TRUE(server.processMessage(*io_message, parse_message,
- response_renderer));
+ // 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));
}
@@ -799,11 +467,12 @@
TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
// The counter should be initialized to 0.
EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
- createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
- RRType::AXFR(), IPPROTO_TCP);
- // It returns false. see AXFRSuccess test.
- EXPECT_FALSE(server.processMessage(*io_message, parse_message,
- response_renderer));
+ 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));
}
@@ -832,10 +501,12 @@
// the program would immediately terminate anyway.
// Create UDP query packet.
- createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
- RRType::NS(), IPPROTO_UDP);
-
- // Modify the message
+ 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);
@@ -844,7 +515,7 @@
getDummyUnknownSocket(), *endpoint);
EXPECT_THROW(server.processMessage(*io_message, parse_message,
- response_renderer),
+ response_obuffer, &dnsserv),
isc::Unexpected);
}
}
Modified: branches/trac347/src/bin/bind10/bind10.py.in
==============================================================================
--- branches/trac347/src/bin/bind10/bind10.py.in (original)
+++ branches/trac347/src/bin/bind10/bind10.py.in Mon Dec 27 11:50:02 2010
@@ -194,8 +194,9 @@
class BoB:
"""Boss of BIND class."""
- def __init__(self, msgq_socket_file=None, auth_port=5300, address=None,
- nocache=False, verbose=False, setuid=None, username=None):
+ def __init__(self, msgq_socket_file=None, dns_port=5300, address=None,
+ forward=None, nocache=False, verbose=False, setuid=None,
+ username=None):
"""
Initialize the Boss of BIND. This is a singleton (only one can run).
@@ -204,7 +205,12 @@
what it is doing.
"""
self.address = address
- self.auth_port = auth_port
+ self.dns_port = dns_port
+ self.forward = forward
+ if forward:
+ self.recursive = True
+ else:
+ self.recursive = False
self.cc_session = None
self.ccs = None
self.cfg_start_auth = True
@@ -417,19 +423,26 @@
Start the Authoritative server
"""
# XXX: this must be read from the configuration manager in the future
- authargs = ['b10-auth', '-p', str(self.auth_port)]
- if self.address:
- authargs += ['-a', str(self.address)]
- if self.nocache:
- authargs += ['-n']
+ if self.recursive:
+ dns_prog = 'b10-recurse'
+ else:
+ dns_prog = 'b10-auth'
+ dnsargs = [dns_prog]
+ if not self.recursive:
+ # The recursive uses configuration manager for these
+ dnsargs += ['-p', str(self.dns_port)]
+ if self.address:
+ dnsargs += ['-a', str(self.address)]
+ if self.nocache:
+ dnsargs += ['-n']
if self.uid:
- authargs += ['-u', str(self.uid)]
- if self.verbose:
- authargs += ['-v']
+ dnsargs += ['-u', str(self.uid)]
+ if self.verbose:
+ dnsargs += ['-v']
# ... and start
- self.start_process("b10-auth", authargs, c_channel_env,
- self.auth_port, self.address)
+ self.start_process("b10-auth", dnsargs, c_channel_env,
+ self.dns_port, self.address)
def start_recurse(self, c_channel_env):
"""
@@ -540,6 +553,7 @@
def stop_all_processes(self):
"""Stop all processes."""
cmd = { "command": ['shutdown']}
+
self.cc_session.group_sendmsg(cmd, 'Cmdctl', 'Cmdctl')
self.cc_session.group_sendmsg(cmd, "ConfigManager", "ConfigManager")
self.cc_session.group_sendmsg(cmd, "Auth", "Auth")
@@ -612,27 +626,44 @@
raise
if pid == 0: break
if pid in self.processes:
+
+ # One of the processes we know about. Get information on it.
proc_info = self.processes.pop(pid)
proc_info.restart_schedule.set_run_stop_time()
self.dead_processes[proc_info.pid] = proc_info
- if self.verbose:
- sys.stdout.write("[bind10] Process %s (PID %d) died.\n" %
- (proc_info.name, proc_info.pid))
- if proc_info.name == "b10-msgq":
- if self.verbose and self.runnable:
+
+ # Write out message, but only if in the running state:
+ # During startup and shutdown, these messages are handled
+ # elsewhere.
+ if self.runnable:
+ if exit_status is None:
+ sys.stdout.write(
+ "[bind10] Process %s (PID %d) died: exit status not available" %
+ (proc_info.name, proc_info.pid))
+ else:
+ sys.stdout.write(
+ "[bind10] Process %s (PID %d) terminated, exit status = %d\n" %
+ (proc_info.name, proc_info.pid, exit_status))
+
+ # Was it a special process?
+ if proc_info.name == "b10-msgq":
sys.stdout.write(
"[bind10] The b10-msgq process died, shutting down.\n")
- self.runnable = False
+ self.runnable = False
else:
sys.stdout.write("[bind10] Unknown child pid %d exited.\n" % pid)
def restart_processes(self):
- """Restart any dead processes.
- Returns the time when the next process is ready to be restarted.
- If the server is shutting down, returns 0.
- If there are no processes, returns None.
- The values returned can be safely passed into select() as the
- timeout value."""
+ """
+ Restart any dead processes:
+
+ * Returns the time when the next process is ready to be restarted.
+ * If the server is shutting down, returns 0.
+ * If there are no processes, returns None.
+
+ The values returned can be safely passed into select() as the
+ timeout value.
+ """
next_restart = None
# if we're shutting down, then don't restart
if not self.runnable:
@@ -649,13 +680,12 @@
else:
if self.verbose:
sys.stdout.write("[bind10] Resurrecting dead %s process...\n" %
- proc_info.name)
+ proc_info.name)
try:
proc_info.respawn()
self.processes[proc_info.pid] = proc_info
- if self.verbose:
- sys.stdout.write("[bind10] Resurrected %s (PID %d)\n" %
- (proc_info.name, proc_info.pid))
+ sys.stdout.write("[bind10] Resurrected %s (PID %d)\n" %
+ (proc_info.name, proc_info.pid))
except:
still_dead[proc_info.pid] = proc_info
# remember any processes that refuse to be resurrected
@@ -697,7 +727,7 @@
a valid port number. Used by OptionParser() on startup."""
try:
if opt_str in ['-p', '--port']:
- parser.values.auth_port = isc.net.parse.port_parse(value)
+ parser.values.dns_port = isc.net.parse.port_parse(value)
else:
raise OptionValueError("Unknown option " + opt_str)
except ValueError as e:
@@ -709,6 +739,8 @@
try:
if opt_str in ['-a', '--address']:
parser.values.address = isc.net.parse.addr_parse(value)
+ elif opt_str in ['-f', '--forward']:
+ parser.values.forward = isc.net.parse.addr_parse(value)
else:
raise OptionValueError("Unknown option " + opt_str)
except ValueError:
@@ -728,17 +760,19 @@
parser = OptionParser(version=VERSION)
parser.add_option("-a", "--address", dest="address", type="string",
action="callback", callback=check_addr, default=None,
- help="address the b10-auth daemon will use (default: listen on all addresses)")
+ help="address the DNS server will use (default: listen on all addresses)")
+ parser.add_option("-f", "--forward", dest="forward", type="string",
+ action="callback", callback=check_addr, default=None,
+ help="nameserver to which DNS queries should be forwarded")
parser.add_option("-m", "--msgq-socket-file", dest="msgq_socket_file",
type="string", default=None,
help="UNIX domain socket file the b10-msgq daemon will use")
parser.add_option("-n", "--no-cache", action="store_true", dest="nocache",
- default=False, help="disable hot-spot cache in b10-auth")
- parser.add_option("-p", "--port", dest="auth_port", type="int",
+ default=False, help="disable hot-spot cache in authoritative DNS server")
+ parser.add_option("-p", "--port", dest="dns_port", type="int",
action="callback", callback=check_port, default=5300,
- help="port the b10-auth daemon will use (default 5300)")
- parser.add_option("-u", "--user", dest="user",
- type="string", default=None,
+ help="port the DNS server will use (default 5300)")
+ parser.add_option("-u", "--user", dest="user", type="string", default=None,
help="Change user after startup (must run as root)")
parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
help="display more about what is going on")
@@ -783,10 +817,6 @@
if options.verbose:
sys.stdout.write("%s\n" % VERSION)
- # TODO: set process name, perhaps by:
- # http://code.google.com/p/procname/
- # http://github.com/lericson/procname/
-
# Create wakeup pipe for signal handlers
wakeup_pipe = os.pipe()
signal.set_wakeup_fd(wakeup_pipe[1])
@@ -802,9 +832,9 @@
signal.signal(signal.SIGPIPE, signal.SIG_IGN)
# Go bob!
- boss_of_bind = BoB(options.msgq_socket_file, options.auth_port,
- options.address, options.nocache, options.verbose,
- setuid, username)
+ boss_of_bind = BoB(options.msgq_socket_file, options.dns_port,
+ options.address, options.forward, options.nocache,
+ options.verbose, setuid, username)
startup_result = boss_of_bind.startup()
if startup_result:
sys.stderr.write("[bind10] Error on startup: %s\n" % startup_result)
Modified: branches/trac347/src/bin/bind10/run_bind10.sh.in
==============================================================================
--- branches/trac347/src/bin/bind10/run_bind10.sh.in (original)
+++ branches/trac347/src/bin/bind10/run_bind10.sh.in Mon Dec 27 11:50:02 2010
@@ -20,10 +20,10 @@
BIND10_PATH=@abs_top_builddir@/src/bin/bind10
-PATH=@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:@abs_top_builddir@/src/bin/recurse:$PATH
+PATH=@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/recurse:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:$PATH
export PATH
-PYTHONPATH=@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs
+PYTHONPATH=@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs
export PYTHONPATH
# If necessary (rare cases), explicitly specify paths to dynamic libraries
@@ -46,5 +46,5 @@
export BIND10_MSGQ_SOCKET_FILE
cd ${BIND10_PATH}
-exec ${PYTHON_EXEC} -O bind10 $*
+exec ${PYTHON_EXEC} -O bind10 "$@"
Modified: branches/trac347/src/bin/bind10/tests/bind10_test.py
==============================================================================
--- branches/trac347/src/bin/bind10/tests/bind10_test.py (original)
+++ branches/trac347/src/bin/bind10/tests/bind10_test.py Mon Dec 27 11:50:02 2010
@@ -78,7 +78,7 @@
bob = BoB()
self.assertEqual(bob.verbose, False)
self.assertEqual(bob.msgq_socket_file, None)
- self.assertEqual(bob.auth_port, 5300)
+ self.assertEqual(bob.dns_port, 5300)
self.assertEqual(bob.address, None)
self.assertEqual(bob.cc_session, None)
self.assertEqual(bob.ccs, None)
@@ -95,7 +95,24 @@
bob = BoB("alt_socket_file")
self.assertEqual(bob.verbose, False)
self.assertEqual(bob.msgq_socket_file, "alt_socket_file")
- self.assertEqual(bob.auth_port, 5300)
+ self.assertEqual(bob.address, None)
+ self.assertEqual(bob.dns_port, 5300)
+ self.assertEqual(bob.cc_session, None)
+ self.assertEqual(bob.ccs, None)
+ self.assertEqual(bob.processes, {})
+ self.assertEqual(bob.dead_processes, {})
+ self.assertEqual(bob.runnable, False)
+ self.assertEqual(bob.uid, None)
+ self.assertEqual(bob.username, None)
+ self.assertEqual(bob.nocache, False)
+ self.assertEqual(bob.cfg_start_auth, True)
+ self.assertEqual(bob.cfg_start_recurse, False)
+
+ def test_init_alternate_dns_port(self):
+ bob = BoB(None, 9999)
+ self.assertEqual(bob.verbose, False)
+ self.assertEqual(bob.msgq_socket_file, None)
+ self.assertEqual(bob.dns_port, 9999)
self.assertEqual(bob.address, None)
self.assertEqual(bob.cc_session, None)
self.assertEqual(bob.ccs, None)
@@ -108,28 +125,11 @@
self.assertEqual(bob.cfg_start_auth, True)
self.assertEqual(bob.cfg_start_recurse, False)
- def test_init_alternate_auth_port(self):
- bob = BoB(None, 9999)
- self.assertEqual(bob.verbose, False)
- self.assertEqual(bob.msgq_socket_file, None)
- self.assertEqual(bob.auth_port, 9999)
- self.assertEqual(bob.address, None)
- self.assertEqual(bob.cc_session, None)
- self.assertEqual(bob.ccs, None)
- self.assertEqual(bob.processes, {})
- self.assertEqual(bob.dead_processes, {})
- self.assertEqual(bob.runnable, False)
- self.assertEqual(bob.uid, None)
- self.assertEqual(bob.username, None)
- self.assertEqual(bob.nocache, False)
- self.assertEqual(bob.cfg_start_auth, True)
- self.assertEqual(bob.cfg_start_recurse, False)
-
def test_init_alternate_address(self):
bob = BoB(None, 1234, IPAddr('127.127.127.127'))
self.assertEqual(bob.verbose, False)
self.assertEqual(bob.msgq_socket_file, None)
- self.assertEqual(bob.auth_port, 1234)
+ self.assertEqual(bob.dns_port, 1234)
self.assertEqual(bob.address.addr, socket.inet_aton('127.127.127.127'))
self.assertEqual(bob.cc_session, None)
self.assertEqual(bob.ccs, None)
Modified: branches/trac347/src/bin/bindctl/bindcmd.py
==============================================================================
--- branches/trac347/src/bin/bindctl/bindcmd.py (original)
+++ branches/trac347/src/bin/bindctl/bindcmd.py Mon Dec 27 11:50:02 2010
@@ -558,7 +558,7 @@
if value_map['type'] in [ 'module', 'map', 'list' ]:
line += "/"
else:
- line += ":\t" + str(value_map['value'])
+ line += ":\t" + json.dumps(value_map['value'])
line += "\t" + value_map['type']
line += "\t"
if value_map['default']:
@@ -569,7 +569,10 @@
elif cmd.command == "add":
self.config_data.add_value(identifier, cmd.params['value'])
elif cmd.command == "remove":
- self.config_data.remove_value(identifier, cmd.params['value'])
+ if 'value' in cmd.params:
+ self.config_data.remove_value(identifier, cmd.params['value'])
+ else:
+ self.config_data.remove_value(identifier, None)
elif cmd.command == "set":
if 'identifier' not in cmd.params:
print("Error: missing identifier or value")
Modified: branches/trac347/src/bin/bindctl/bindctl-source.py.in
==============================================================================
--- branches/trac347/src/bin/bindctl/bindctl-source.py.in (original)
+++ branches/trac347/src/bin/bindctl/bindctl-source.py.in Mon Dec 27 11:50:02 2010
@@ -28,7 +28,10 @@
isc.util.process.rename()
-__version__ = 'Bindctl'
+# This is the version that gets displayed to the user.
+# The VERSION string consists of the module name, the module version
+# number, and the overall BIND 10 version number (set in configure.ac).
+VERSION = "bindctl 20101201 (BIND 10 @PACKAGE_VERSION@)"
def prepare_config_commands(tool):
'''Prepare fixed commands for local configuration editing'''
@@ -48,7 +51,7 @@
cmd = CommandInfo(name = "remove", desc = "Remove entry from configuration list")
param = ParamInfo(name = "identifier", type = "string", optional=True)
cmd.add_param(param)
- param = ParamInfo(name = "value", type = "string", optional=False)
+ param = ParamInfo(name = "value", type = "string", optional=True)
cmd.add_param(param)
module.add_command(cmd)
@@ -113,7 +116,7 @@
if __name__ == '__main__':
try:
- parser = OptionParser(version = __version__)
+ parser = OptionParser(version = VERSION)
set_bindctl_options(parser)
(options, args) = parser.parse_args()
server_addr = options.addr + ':' + str(options.port)
Modified: branches/trac347/src/bin/bindctl/run_bindctl.sh.in
==============================================================================
--- branches/trac347/src/bin/bindctl/run_bindctl.sh.in (original)
+++ branches/trac347/src/bin/bindctl/run_bindctl.sh.in Mon Dec 27 11:50:02 2010
@@ -26,5 +26,8 @@
B10_FROM_SOURCE=@abs_top_srcdir@
export B10_FROM_SOURCE
+BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
+export BIND10_MSGQ_SOCKET_FILE
+
cd ${BINDCTL_PATH}
-exec ${PYTHON_EXEC} -O bindctl $*
+exec ${PYTHON_EXEC} -O bindctl "$@"
Modified: branches/trac347/src/bin/cmdctl/run_b10-cmdctl.sh.in
==============================================================================
--- branches/trac347/src/bin/cmdctl/run_b10-cmdctl.sh.in (original)
+++ branches/trac347/src/bin/cmdctl/run_b10-cmdctl.sh.in Mon Dec 27 11:50:02 2010
@@ -22,6 +22,8 @@
PYTHONPATH=@abs_top_srcdir@/src/lib/python
export PYTHONPATH
+BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
+export BIND10_MSGQ_SOCKET_FILE
+
cd ${CMD_CTRLD_PATH}
-${PYTHON_EXEC} b10-cmdctl
-
+exec ${PYTHON_EXEC} b10-cmdctl "$@"
Modified: branches/trac347/src/bin/loadzone/run_loadzone.sh.in
==============================================================================
--- branches/trac347/src/bin/loadzone/run_loadzone.sh.in (original)
+++ branches/trac347/src/bin/loadzone/run_loadzone.sh.in Mon Dec 27 11:50:02 2010
@@ -21,5 +21,8 @@
PYTHONPATH=@abs_top_builddir@/src/lib/python
export PYTHONPATH
+BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
+export BIND10_MSGQ_SOCKET_FILE
+
LOADZONE_PATH=@abs_top_builddir@/src/bin/loadzone
-exec ${LOADZONE_PATH}/b10-loadzone $*
+exec ${LOADZONE_PATH}/b10-loadzone "$@"
Modified: branches/trac347/src/bin/msgq/msgq.py.in
==============================================================================
--- branches/trac347/src/bin/msgq/msgq.py.in (original)
+++ branches/trac347/src/bin/msgq/msgq.py.in Mon Dec 27 11:50:02 2010
@@ -38,7 +38,9 @@
isc.util.process.rename()
# This is the version that gets displayed to the user.
-__version__ = "v20091030 (Paving the DNS Parking Lot)"
+# The VERSION string consists of the module name, the module version
+# number, and the overall BIND 10 version number (set in configure.ac).
+VERSION = "b10-msgq 20100818 (BIND 10 @PACKAGE_VERSION@)"
class MsgQReceiveError(Exception): pass
@@ -421,7 +423,7 @@
parser.values.msgq_port = intval
# Parse any command-line options.
- parser = OptionParser(version=__version__)
+ parser = OptionParser(version=VERSION)
parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
help="display more about what is going on")
parser.add_option("-s", "--socket-file", dest="msgq_socket_file",
@@ -433,7 +435,7 @@
# Announce startup.
if options.verbose:
- sys.stdout.write("[b10-msgq] MsgQ %s\n" % __version__)
+ sys.stdout.write("[b10-msgq] %s\n" % VERSION)
msgq = MsgQ(options.msgq_socket_file, options.verbose)
Modified: branches/trac347/src/bin/msgq/run_msgq.sh.in
==============================================================================
--- branches/trac347/src/bin/msgq/run_msgq.sh.in (original)
+++ branches/trac347/src/bin/msgq/run_msgq.sh.in Mon Dec 27 11:50:02 2010
@@ -23,5 +23,8 @@
PYTHONPATH=@abs_top_srcdir@/src/lib/python
export PYTHONPATH
+BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
+export BIND10_MSGQ_SOCKET_FILE
+
cd ${MYPATH_PATH}
-exec ${PYTHON_EXEC} -O b10-msgq $*
+exec ${PYTHON_EXEC} -O b10-msgq "$@"
Modified: branches/trac347/src/bin/stats/run_b10-stats.sh.in
==============================================================================
--- branches/trac347/src/bin/stats/run_b10-stats.sh.in (original)
+++ branches/trac347/src/bin/stats/run_b10-stats.sh.in Mon Dec 27 11:50:02 2010
@@ -21,10 +21,13 @@
PYTHONPATH=@abs_top_builddir@/src/lib/python
export PYTHONPATH
+BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
+export BIND10_MSGQ_SOCKET_FILE
+
B10_FROM_BUILD=@abs_top_builddir@
export B10_FROM_BUILD
STATS_PATH=@abs_top_builddir@/src/bin/stats
cd ${STATS_PATH}
-exec ${PYTHON_EXEC} -O b10-stats $*
+exec ${PYTHON_EXEC} -O b10-stats "$@"
Modified: branches/trac347/src/bin/stats/run_b10-stats_stub.sh.in
==============================================================================
--- branches/trac347/src/bin/stats/run_b10-stats_stub.sh.in (original)
+++ branches/trac347/src/bin/stats/run_b10-stats_stub.sh.in Mon Dec 27 11:50:02 2010
@@ -24,7 +24,10 @@
B10_FROM_BUILD=@abs_top_srcdir@
export B10_FROM_BUILD
+BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
+export BIND10_MSGQ_SOCKET_FILE
+
STATS_PATH=@abs_top_builddir@/src/bin/stats
cd ${STATS_PATH}
-exec ${PYTHON_EXEC} -O b10-stats_stub $*
+exec ${PYTHON_EXEC} -O b10-stats_stub "$@"
Modified: branches/trac347/src/bin/usermgr/run_b10-cmdctl-usermgr.sh.in
==============================================================================
--- branches/trac347/src/bin/usermgr/run_b10-cmdctl-usermgr.sh.in (original)
+++ branches/trac347/src/bin/usermgr/run_b10-cmdctl-usermgr.sh.in Mon Dec 27 11:50:02 2010
@@ -20,6 +20,8 @@
MYPATH_PATH=@abs_top_builddir@/src/bin/usermgr
+BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
+export BIND10_MSGQ_SOCKET_FILE
+
cd ${MYPATH_PATH}
-${PYTHON_EXEC} b10-cmdctl-usermgr
-
+exec ${PYTHON_EXEC} b10-cmdctl-usermgr "$@"
Modified: branches/trac347/src/bin/xfrin/b10-xfrin.xml
==============================================================================
--- branches/trac347/src/bin/xfrin/b10-xfrin.xml (original)
+++ branches/trac347/src/bin/xfrin/b10-xfrin.xml Mon Dec 27 11:50:02 2010
@@ -149,7 +149,6 @@
the authoritative server to transfer from,
and <varname>port</varname> to define the port number on the
authoritative server (defaults to 53).
-<!-- TODO: note: not documenting db_file since that will be removed. -->
</para>
<!-- TODO: later hostname for master? -->
Modified: branches/trac347/src/bin/xfrin/run_b10-xfrin.sh.in
==============================================================================
--- branches/trac347/src/bin/xfrin/run_b10-xfrin.sh.in (original)
+++ branches/trac347/src/bin/xfrin/run_b10-xfrin.sh.in Mon Dec 27 11:50:02 2010
@@ -22,6 +22,8 @@
PYTHONPATH=@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/dns/.libs
export PYTHONPATH
+BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
+export BIND10_MSGQ_SOCKET_FILE
+
cd ${MYPATH_PATH}
-${PYTHON_EXEC} b10-xfrin
-
+exec ${PYTHON_EXEC} b10-xfrin "$@"
Modified: branches/trac347/src/bin/xfrout/b10-xfrout.8
==============================================================================
--- branches/trac347/src/bin/xfrout/b10-xfrout.8 (original)
+++ branches/trac347/src/bin/xfrout/b10-xfrout.8 Mon Dec 27 11:50:02 2010
@@ -2,12 +2,12 @@
.\" Title: b10-xfrout
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: September 8, 2010
+.\" Date: December 1, 2010
.\" Manual: BIND10
.\" Source: BIND10
.\" Language: English
.\"
-.TH "B10\-XFROUT" "8" "September 8, 2010" "BIND10" "BIND10"
+.TH "B10\-XFROUT" "8" "December 1, 2010" "BIND10" "BIND10"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
@@ -67,11 +67,6 @@
The configurable settings are:
.PP
-\fIdb_file\fR
-defines the path to the SQLite3 data store file\&. The default is
-/usr/local/var/bind10\-devel/zone\&.sqlite3\&.
-.PP
-
\fItransfers_out\fR
defines the maximum number of outgoing zone transfers that can run concurrently\&. The default is 10\&.
.if n \{\
Modified: branches/trac347/src/bin/xfrout/b10-xfrout.xml
==============================================================================
--- branches/trac347/src/bin/xfrout/b10-xfrout.xml (original)
+++ branches/trac347/src/bin/xfrout/b10-xfrout.xml Mon Dec 27 11:50:02 2010
@@ -21,7 +21,7 @@
<refentry>
<refentryinfo>
- <date>September 8, 2010</date>
+ <date>December 1, 2010</date>
</refentryinfo>
<refmeta>
@@ -92,13 +92,6 @@
<title>CONFIGURATION AND COMMANDS</title>
<para>
The configurable settings are:
- </para>
- <para>
- <varname>db_file</varname>
- defines the path to the SQLite3 data store file.
- The default is
- <filename>/usr/local/var/bind10-devel/zone.sqlite3</filename>.
-<!-- TODO: db_file will be removed -->
</para>
<para>
<varname>transfers_out</varname>
Modified: branches/trac347/src/bin/xfrout/run_b10-xfrout.sh.in
==============================================================================
--- branches/trac347/src/bin/xfrout/run_b10-xfrout.sh.in (original)
+++ branches/trac347/src/bin/xfrout/run_b10-xfrout.sh.in Mon Dec 27 11:50:02 2010
@@ -22,6 +22,8 @@
PYTHONPATH=@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/dns/python/.libs
export PYTHONPATH
+BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
+export BIND10_MSGQ_SOCKET_FILE
+
cd ${MYPATH_PATH}
-${PYTHON_EXEC} b10-xfrout
-
+exec ${PYTHON_EXEC} b10-xfrout "$@"
Modified: branches/trac347/src/bin/xfrout/xfrout.spec.pre.in
==============================================================================
--- branches/trac347/src/bin/xfrout/xfrout.spec.pre.in (original)
+++ branches/trac347/src/bin/xfrout/xfrout.spec.pre.in Mon Dec 27 11:50:02 2010
@@ -7,12 +7,6 @@
"item_type": "integer",
"item_optional": false,
"item_default": 10
- },
- {
- "item_name": "db_file",
- "item_type": "string",
- "item_optional": false,
- "item_default": "@@LOCALSTATEDIR@@/@PACKAGE@/zone.sqlite3"
},
{
"item_name": "log_name",
Modified: branches/trac347/src/bin/zonemgr/run_b10-zonemgr.sh.in
==============================================================================
--- branches/trac347/src/bin/zonemgr/run_b10-zonemgr.sh.in (original)
+++ branches/trac347/src/bin/zonemgr/run_b10-zonemgr.sh.in Mon Dec 27 11:50:02 2010
@@ -22,6 +22,8 @@
PYTHONPATH=@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/dns/.libs
export PYTHONPATH
+BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
+export BIND10_MSGQ_SOCKET_FILE
+
cd ${MYPATH_PATH}
-${PYTHON_EXEC} b10-zonemgr
-
+exec ${PYTHON_EXEC} b10-zonemgr "$@"
Modified: branches/trac347/src/lib/Makefile.am
==============================================================================
--- branches/trac347/src/lib/Makefile.am (original)
+++ branches/trac347/src/lib/Makefile.am Mon Dec 27 11:50:02 2010
@@ -1,1 +1,2 @@
-SUBDIRS = exceptions dns cc config datasrc python xfr bench
+SUBDIRS = exceptions dns cc config datasrc python xfr bench log asiolink \
+ testutils nsas
Modified: branches/trac347/src/lib/asiolink/asiolink.cc
==============================================================================
--- branches/trac347/src/lib/asiolink/asiolink.cc (original)
+++ branches/trac347/src/lib/asiolink/asiolink.cc Mon Dec 27 11:50:02 2010
@@ -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/trac347/src/lib/asiolink/asiolink.h
==============================================================================
--- branches/trac347/src/lib/asiolink/asiolink.h (original)
+++ branches/trac347/src/lib/asiolink/asiolink.h Mon Dec 27 11:50:02 2010
@@ -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
@@ -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/trac347/src/lib/asiolink/tests/asiolink_unittest.cc
==============================================================================
--- branches/trac347/src/lib/asiolink/tests/asiolink_unittest.cc (original)
+++ branches/trac347/src/lib/asiolink/tests/asiolink_unittest.cc Mon Dec 27 11:50:02 2010
@@ -21,6 +21,7 @@
#include <boost/lexical_cast.hpp>
#include <boost/bind.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <gtest/gtest.h>
@@ -54,6 +55,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");
@@ -710,4 +714,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/trac347/src/lib/cc/session.cc
==============================================================================
--- branches/trac347/src/lib/cc/session.cc (original)
+++ branches/trac347/src/lib/cc/session.cc Mon Dec 27 11:50:02 2010
@@ -171,7 +171,7 @@
asio::async_read(socket_, asio::buffer(data, datalen),
boost::bind(&setResult, &read_result, _1));
asio::deadline_timer timer(socket_.io_service());
-
+
if (getTimeout() != 0) {
timer.expires_from_now(boost::posix_time::milliseconds(getTimeout()));
timer.async_wait(boost::bind(&setResult, &timer_result, _1));
Modified: branches/trac347/src/lib/config/module_spec.cc
==============================================================================
--- branches/trac347/src/lib/config/module_spec.cc (original)
+++ branches/trac347/src/lib/config/module_spec.cc Mon Dec 27 11:50:02 2010
@@ -330,14 +330,33 @@
ModuleSpec::validate_spec_list(ConstElementPtr spec, ConstElementPtr data,
const bool full, ElementPtr errors) const
{
+ bool validated = true;
std::string cur_item_name;
BOOST_FOREACH(ConstElementPtr cur_spec_el, spec->listValue()) {
if (!validate_spec(cur_spec_el, data, full, errors)) {
- return (false);
- }
- }
- return (true);
-}
-
-}
-}
+ validated = false;
+ }
+ }
+
+ typedef std::pair<std::string, ConstElementPtr> maptype;
+
+ BOOST_FOREACH(maptype m, data->mapValue()) {
+ bool found = false;
+ BOOST_FOREACH(ConstElementPtr cur_spec_el, spec->listValue()) {
+ if (cur_spec_el->get("item_name")->stringValue().compare(m.first) == 0) {
+ found = true;
+ }
+ }
+ if (!found) {
+ validated = false;
+ if (errors) {
+ errors->add(Element::create("Unknown item " + m.first));
+ }
+ }
+ }
+
+ return (validated);
+}
+
+}
+}
Modified: branches/trac347/src/lib/config/tests/module_spec_unittests.cc
==============================================================================
--- branches/trac347/src/lib/config/tests/module_spec_unittests.cc (original)
+++ branches/trac347/src/lib/config/tests/module_spec_unittests.cc Mon Dec 27 11:50:02 2010
@@ -166,8 +166,13 @@
EXPECT_TRUE(data_test(dd, "data22_6.data"));
EXPECT_TRUE(data_test(dd, "data22_7.data"));
EXPECT_FALSE(data_test(dd, "data22_8.data"));
+ EXPECT_FALSE(data_test(dd, "data22_9.data"));
ElementPtr errors = Element::createList();
EXPECT_FALSE(data_test_with_errors(dd, "data22_8.data", errors));
EXPECT_EQ("[ \"Type mismatch\" ]", errors->str());
+
+ errors = Element::createList();
+ EXPECT_FALSE(data_test_with_errors(dd, "data22_9.data", errors));
+ EXPECT_EQ("[ \"Unknown item value_does_not_exist\" ]", errors->str());
}
Modified: branches/trac347/src/lib/config/tests/testdata/Makefile.am
==============================================================================
--- branches/trac347/src/lib/config/tests/testdata/Makefile.am (original)
+++ branches/trac347/src/lib/config/tests/testdata/Makefile.am Mon Dec 27 11:50:02 2010
@@ -20,6 +20,7 @@
EXTRA_DIST += data22_6.data
EXTRA_DIST += data22_7.data
EXTRA_DIST += data22_8.data
+EXTRA_DIST += data22_9.data
EXTRA_DIST += spec1.spec
EXTRA_DIST += spec2.spec
EXTRA_DIST += spec3.spec
Modified: branches/trac347/src/lib/config/tests/testdata/data22_8.data
==============================================================================
--- branches/trac347/src/lib/config/tests/testdata/data22_8.data (original)
+++ branches/trac347/src/lib/config/tests/testdata/data22_8.data Mon Dec 27 11:50:02 2010
@@ -5,5 +5,6 @@
"value4": "foo",
"value5": [ 1, 2, 3 ],
"value6": { "v61": "bar", "v62": true },
- "value8": [ { "a": "d" }, { "a": 1 } ]
+ "value8": [ { "a": "d" }, { "a": 1 } ],
+ "value9": { "v91": "hi", "v92": { "v92a": "Hi", "v92b": 3 } }
}
Modified: branches/trac347/src/lib/datasrc/Makefile.am
==============================================================================
--- branches/trac347/src/lib/datasrc/Makefile.am (original)
+++ branches/trac347/src/lib/datasrc/Makefile.am Mon Dec 27 11:50:02 2010
@@ -15,4 +15,8 @@
libdatasrc_la_SOURCES += sqlite3_datasrc.h sqlite3_datasrc.cc
libdatasrc_la_SOURCES += query.h query.cc
libdatasrc_la_SOURCES += cache.h cache.cc
+libdatasrc_la_SOURCES += rbtree.h
libdatasrc_la_SOURCES += zonetable.h zonetable.cc
+libdatasrc_la_SOURCES += memory_datasrc.h memory_datasrc.cc
+libdatasrc_la_SOURCES += zone.h
+libdatasrc_la_SOURCES += result.h
Modified: branches/trac347/src/lib/datasrc/data_source.h
==============================================================================
--- branches/trac347/src/lib/datasrc/data_source.h (original)
+++ branches/trac347/src/lib/datasrc/data_source.h Mon Dec 27 11:50:02 2010
@@ -248,7 +248,7 @@
void addDataSrc(ConstDataSrcPtr data_src);
void removeDataSrc(ConstDataSrcPtr data_src);
size_t dataSrcCount() { return (data_sources.size()); }
-
+
void findClosestEnclosure(DataSrcMatch& match) const;
// Actual queries for data should not be sent to a MetaDataSrc object,
Modified: branches/trac347/src/lib/datasrc/static_datasrc.cc
==============================================================================
--- branches/trac347/src/lib/datasrc/static_datasrc.cc (original)
+++ branches/trac347/src/lib/datasrc/static_datasrc.cc Mon Dec 27 11:50:02 2010
@@ -79,6 +79,7 @@
authors->addRdata(generic::TXT("JINMEI Tatuya"));
authors->addRdata(generic::TXT("Kazunori Fujiwara"));
authors->addRdata(generic::TXT("Michael Graff"));
+ authors->addRdata(generic::TXT("Michal Vaner"));
authors->addRdata(generic::TXT("Naoki Kambe"));
authors->addRdata(generic::TXT("Shane Kerr"));
authors->addRdata(generic::TXT("Shen Tingting"));
Modified: branches/trac347/src/lib/datasrc/tests/Makefile.am
==============================================================================
--- branches/trac347/src/lib/datasrc/tests/Makefile.am (original)
+++ branches/trac347/src/lib/datasrc/tests/Makefile.am Mon Dec 27 11:50:02 2010
@@ -24,12 +24,14 @@
run_unittests_SOURCES += query_unittest.cc
run_unittests_SOURCES += cache_unittest.cc
run_unittests_SOURCES += test_datasrc.h test_datasrc.cc
+run_unittests_SOURCES += rbtree_unittest.cc
run_unittests_SOURCES += zonetable_unittest.cc
+run_unittests_SOURCES += memory_datasrc_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(SQLITE_LIBS)
-run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/libdatasrc.la
+run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/libdatasrc.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
Modified: branches/trac347/src/lib/datasrc/tests/datasrc_unittest.cc
==============================================================================
--- branches/trac347/src/lib/datasrc/tests/datasrc_unittest.cc (original)
+++ branches/trac347/src/lib/datasrc/tests/datasrc_unittest.cc Mon Dec 27 11:50:02 2010
@@ -877,10 +877,30 @@
EXPECT_TRUE(it->isLast());
}
-TEST_F(DataSrcTest, RootDSQuery) {
+// Test sending a DS query to root (nonsense, but it should survive)
+TEST_F(DataSrcTest, RootDSQuery1) {
EXPECT_NO_THROW(createAndProcessQuery(Name("."), RRClass::IN(),
RRType::DS()));
headerCheck(msg, Rcode::REFUSED(), true, false, true, 0, 0, 0);
+}
+
+// The same, but when we have the root zone
+// (which triggers rfc4035 section 3.1.4.1)
+TEST_F(DataSrcTest, RootDSQuery2) {
+ // The message
+ msg.makeResponse();
+ msg.setOpcode(Opcode::QUERY());
+ msg.addQuestion(Question(Name("."), RRClass::IN(), RRType::DS()));
+ msg.setHeaderFlag(Message::HEADERFLAG_RD);
+ // Prepare the source
+ DataSrcPtr sql3_source = DataSrcPtr(new Sqlite3DataSrc);
+ ConstElementPtr sqlite_root = Element::fromJSON(
+ "{ \"database_file\": \"" TEST_DATA_DIR "/test-root.sqlite3\"}");
+ EXPECT_NO_THROW(sql3_source->init(sqlite_root));
+ // Make the query
+ EXPECT_NO_THROW(performQuery(*sql3_source, cache, msg));
+
+ headerCheck(msg, Rcode::NOERROR(), true, true, true, 0, 1, 0);
}
TEST_F(DataSrcTest, DSQueryFromCache) {
Modified: branches/trac347/src/lib/datasrc/tests/static_unittest.cc
==============================================================================
--- branches/trac347/src/lib/datasrc/tests/static_unittest.cc (original)
+++ branches/trac347/src/lib/datasrc/tests/static_unittest.cc Mon Dec 27 11:50:02 2010
@@ -63,6 +63,7 @@
authors_data.push_back("JINMEI Tatuya");
authors_data.push_back("Kazunori Fujiwara");
authors_data.push_back("Michael Graff");
+ authors_data.push_back("Michal Vaner");
authors_data.push_back("Naoki Kambe");
authors_data.push_back("Shane Kerr");
authors_data.push_back("Shen Tingting");
Modified: branches/trac347/src/lib/datasrc/tests/zonetable_unittest.cc
==============================================================================
--- branches/trac347/src/lib/datasrc/tests/zonetable_unittest.cc (original)
+++ branches/trac347/src/lib/datasrc/tests/zonetable_unittest.cc Mon Dec 27 11:50:02 2010
@@ -18,6 +18,8 @@
#include <dns/rrclass.h>
#include <datasrc/zonetable.h>
+// We use MemoryZone to put something into the table
+#include <datasrc/memory_datasrc.h>
#include <gtest/gtest.h>
@@ -26,79 +28,88 @@
namespace {
TEST(ZoneTest, init) {
- Zone zone(RRClass::IN(), Name("example.com"));
+ MemoryZone zone(RRClass::IN(), Name("example.com"));
EXPECT_EQ(Name("example.com"), zone.getOrigin());
EXPECT_EQ(RRClass::IN(), zone.getClass());
- Zone ch_zone(RRClass::CH(), Name("example"));
+ MemoryZone ch_zone(RRClass::CH(), Name("example"));
EXPECT_EQ(Name("example"), ch_zone.getOrigin());
EXPECT_EQ(RRClass::CH(), ch_zone.getClass());
}
+TEST(ZoneTest, find) {
+ MemoryZone zone(RRClass::IN(), Name("example.com"));
+ EXPECT_EQ(Zone::NXDOMAIN,
+ zone.find(Name("www.example.com"), RRType::A()).code);
+}
+
class ZoneTableTest : public ::testing::Test {
protected:
- ZoneTableTest() : zone1(new Zone(RRClass::IN(), Name("example.com"))),
- zone2(new Zone(RRClass::IN(), Name("example.net"))),
- zone3(new Zone(RRClass::IN(), Name("example")))
+ ZoneTableTest() : zone1(new MemoryZone(RRClass::IN(),
+ Name("example.com"))),
+ zone2(new MemoryZone(RRClass::IN(),
+ Name("example.net"))),
+ zone3(new MemoryZone(RRClass::IN(), Name("example")))
{}
ZoneTable zone_table;
ZonePtr zone1, zone2, zone3;
};
-TEST_F(ZoneTableTest, add) {
- EXPECT_EQ(ZoneTable::SUCCESS, zone_table.add(zone1));
- EXPECT_EQ(ZoneTable::EXIST, zone_table.add(zone1));
+TEST_F(ZoneTableTest, addZone) {
+ EXPECT_EQ(result::SUCCESS, zone_table.addZone(zone1));
+ EXPECT_EQ(result::EXIST, zone_table.addZone(zone1));
// names are compared in a case insensitive manner.
- EXPECT_EQ(ZoneTable::EXIST, zone_table.add(
- ZonePtr(new Zone(RRClass::IN(), Name("EXAMPLE.COM")))));
+ EXPECT_EQ(result::EXIST, zone_table.addZone(
+ ZonePtr(new MemoryZone(RRClass::IN(), Name("EXAMPLE.COM")))));
- EXPECT_EQ(ZoneTable::SUCCESS, zone_table.add(zone2));
- EXPECT_EQ(ZoneTable::SUCCESS, zone_table.add(zone3));
+ EXPECT_EQ(result::SUCCESS, zone_table.addZone(zone2));
+ EXPECT_EQ(result::SUCCESS, zone_table.addZone(zone3));
// Zone table is indexed only by name. Duplicate origin name with
// different zone class isn't allowed.
- EXPECT_EQ(ZoneTable::EXIST, zone_table.add(
- ZonePtr(new Zone(RRClass::CH(), Name("example.com")))));
+ EXPECT_EQ(result::EXIST, zone_table.addZone(
+ ZonePtr(new MemoryZone(RRClass::CH(),
+ Name("example.com")))));
/// Bogus zone (NULL)
- EXPECT_THROW(zone_table.add(ZonePtr()), isc::InvalidParameter);
+ EXPECT_THROW(zone_table.addZone(ZonePtr()), isc::InvalidParameter);
}
-TEST_F(ZoneTableTest, remove) {
- EXPECT_EQ(ZoneTable::SUCCESS, zone_table.add(zone1));
- EXPECT_EQ(ZoneTable::SUCCESS, zone_table.add(zone2));
- EXPECT_EQ(ZoneTable::SUCCESS, zone_table.add(zone3));
+TEST_F(ZoneTableTest, DISABLED_removeZone) {
+ EXPECT_EQ(result::SUCCESS, zone_table.addZone(zone1));
+ EXPECT_EQ(result::SUCCESS, zone_table.addZone(zone2));
+ EXPECT_EQ(result::SUCCESS, zone_table.addZone(zone3));
- EXPECT_EQ(ZoneTable::SUCCESS, zone_table.remove(Name("example.net")));
- EXPECT_EQ(ZoneTable::NOTFOUND, zone_table.remove(Name("example.net")));
+ EXPECT_EQ(result::SUCCESS, zone_table.removeZone(Name("example.net")));
+ EXPECT_EQ(result::NOTFOUND, zone_table.removeZone(Name("example.net")));
}
-TEST_F(ZoneTableTest, find) {
- EXPECT_EQ(ZoneTable::SUCCESS, zone_table.add(zone1));
- EXPECT_EQ(ZoneTable::SUCCESS, zone_table.add(zone2));
- EXPECT_EQ(ZoneTable::SUCCESS, zone_table.add(zone3));
+TEST_F(ZoneTableTest, findZone) {
+ EXPECT_EQ(result::SUCCESS, zone_table.addZone(zone1));
+ EXPECT_EQ(result::SUCCESS, zone_table.addZone(zone2));
+ EXPECT_EQ(result::SUCCESS, zone_table.addZone(zone3));
- EXPECT_EQ(ZoneTable::SUCCESS, zone_table.find(Name("example.com")).code);
+ EXPECT_EQ(result::SUCCESS, zone_table.findZone(Name("example.com")).code);
EXPECT_EQ(Name("example.com"),
- zone_table.find(Name("example.com")).zone->getOrigin());
+ zone_table.findZone(Name("example.com")).zone->getOrigin());
- EXPECT_EQ(ZoneTable::NOTFOUND,
- zone_table.find(Name("example.org")).code);
- EXPECT_EQ(static_cast<const Zone*>(NULL),
- zone_table.find(Name("example.org")).zone);
+ EXPECT_EQ(result::NOTFOUND,
+ zone_table.findZone(Name("example.org")).code);
+ EXPECT_EQ(ConstZonePtr(),
+ zone_table.findZone(Name("example.org")).zone);
// there's no exact match. the result should be the longest match,
// and the code should be PARTIALMATCH.
- EXPECT_EQ(ZoneTable::PARTIALMATCH,
- zone_table.find(Name("www.example.com")).code);
+ EXPECT_EQ(result::PARTIALMATCH,
+ zone_table.findZone(Name("www.example.com")).code);
EXPECT_EQ(Name("example.com"),
- zone_table.find(Name("www.example.com")).zone->getOrigin());
+ zone_table.findZone(Name("www.example.com")).zone->getOrigin());
// make sure the partial match is indeed the longest match by adding
// a zone with a shorter origin and query again.
- ZonePtr zone_com(new Zone(RRClass::IN(), Name("com")));
- EXPECT_EQ(ZoneTable::SUCCESS, zone_table.add(zone_com));
+ ZonePtr zone_com(new MemoryZone(RRClass::IN(), Name("com")));
+ EXPECT_EQ(result::SUCCESS, zone_table.addZone(zone_com));
EXPECT_EQ(Name("example.com"),
- zone_table.find(Name("www.example.com")).zone->getOrigin());
+ zone_table.findZone(Name("www.example.com")).zone->getOrigin());
}
}
Modified: branches/trac347/src/lib/datasrc/zonetable.cc
==============================================================================
--- branches/trac347/src/lib/datasrc/zonetable.cc (original)
+++ branches/trac347/src/lib/datasrc/zonetable.cc Mon Dec 27 11:50:02 2010
@@ -12,15 +12,12 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-// Note: map and utility (for 'pair') are for temporary workaround.
-// we'll soon replace them with built-in intelligent backend structure.
-#include <map>
-#include <utility>
+#include <cassert>
#include <dns/name.h>
-#include <dns/rrclass.h>
#include <datasrc/zonetable.h>
+#include <datasrc/rbtree.h>
using namespace std;
using namespace isc::dns;
@@ -28,39 +25,77 @@
namespace isc {
namespace datasrc {
-struct Zone::ZoneImpl {
- ZoneImpl(const RRClass& zone_class, const Name& origin) :
- zone_class_(zone_class), origin_(origin)
- {}
- RRClass zone_class_;
- Name origin_;
-};
+/// \short Private data and implementation of ZoneTable
+struct ZoneTable::ZoneTableImpl {
+ // Type aliases to make it shorter
+ typedef RBTree<Zone> ZoneTree;
+ typedef RBNode<Zone> ZoneNode;
+ // The actual storage
+ ZoneTree zones_;
-Zone::Zone(const RRClass& zone_class, const Name& origin) : impl_(NULL) {
- impl_ = new ZoneImpl(zone_class, origin);
-}
+ /*
+ * The implementation methods are here and just wrap-called in the
+ * ZoneTable. We have variables locally (without impl_->), have
+ * type aliases, etc. And they will get inlined anyway.
+ */
-Zone::~Zone() {
- delete impl_;
-}
+ // Implementation of ZoneTable::addZone
+ result::Result addZone(ZonePtr zone) {
+ // Sanity check
+ if (!zone) {
+ isc_throw(InvalidParameter,
+ "Null pointer is passed to ZoneTable::addZone()");
+ }
-const Name&
-Zone::getOrigin() const {
- return (impl_->origin_);
-}
+ // Get the node where we put the zone
+ ZoneNode* node(NULL);
+ switch (zones_.insert(zone->getOrigin(), &node)) {
+ // This is OK
+ case ZoneTree::SUCCEED:
+ case ZoneTree::ALREADYEXIST:
+ break;
+ // Can Not Happen
+ default:
+ assert(0);
+ }
+ // Can Not Happen
+ assert(node);
-const RRClass&
-Zone::getClass() const {
- return (impl_->zone_class_);
-}
+ // Is it empty? We either just created it or it might be nonterminal
+ if (node->isEmpty()) {
+ node->setData(zone);
+ return (result::SUCCESS);
+ } else { // There's something there already
+ return (result::EXIST);
+ }
+ }
-// This is a temporary, inefficient implementation using std::map and handmade
-// iteration to realize longest match.
+ // Implementation of ZoneTable::findZone
+ ZoneTable::FindResult findZone(const Name& name) const {
+ ZoneNode *node(NULL);
+ result::Result my_result;
-struct ZoneTable::ZoneTableImpl {
- typedef map<Name, ZonePtr> ZoneMap;
- typedef pair<Name, ZonePtr> NameAndZone;
- ZoneMap zones;
+ // Translate the return codes
+ switch (zones_.find(name, &node)) {
+ case ZoneTree::EXACTMATCH:
+ my_result = result::SUCCESS;
+ break;
+ case ZoneTree::PARTIALMATCH:
+ my_result = result::PARTIALMATCH;
+ break;
+ // We have no data there, so translate the pointer to NULL as well
+ case ZoneTree::NOTFOUND:
+ return (FindResult(result::NOTFOUND, ConstZonePtr()));
+ // Can Not Happen
+ default:
+ assert(0);
+ }
+
+ // Can Not Happen (remember, NOTFOUND is handled)
+ assert(node);
+
+ return (FindResult(my_result, node->getData()));
+ }
};
ZoneTable::ZoneTable() : impl_(new ZoneTableImpl)
@@ -70,41 +105,23 @@
delete impl_;
}
-ZoneTable::Result
-ZoneTable::add(ZonePtr zone) {
- if (!zone) {
- isc_throw(InvalidParameter,
- "Null pointer is passed to ZoneTable::add()");
- }
-
- if (impl_->zones.insert(
- ZoneTableImpl::NameAndZone(zone->getOrigin(), zone)).second
- == true) {
- return (SUCCESS);
- } else {
- return (EXIST);
- }
+result::Result
+ZoneTable::addZone(ZonePtr zone) {
+ return (impl_->addZone(zone));
}
-ZoneTable::Result
-ZoneTable::remove(const Name& origin) {
- return (impl_->zones.erase(origin) == 1 ? SUCCESS : NOTFOUND);
+result::Result
+ZoneTable::removeZone(const Name&) {
+ // TODO Implement
+ assert(0);
+ // This should not ever be returned, the assert should kill us by now
+ return (result::SUCCESS);
}
ZoneTable::FindResult
-ZoneTable::find(const Name& name) const {
- // Inefficient internal loop to find a longest match.
- // This will be replaced with a single call to more intelligent backend.
- for (int i = 0; i < name.getLabelCount(); ++i) {
- Name matchname(name.split(i));
- ZoneTableImpl::ZoneMap::const_iterator found =
- impl_->zones.find(matchname);
- if (found != impl_->zones.end()) {
- return (FindResult(i == 0 ? SUCCESS : PARTIALMATCH,
- (*found).second.get()));
- }
- }
- return (FindResult(NOTFOUND, NULL));
+ZoneTable::findZone(const Name& name) const {
+ return (impl_->findZone(name));
}
+
} // end of namespace datasrc
} // end of namespace isc
Modified: branches/trac347/src/lib/datasrc/zonetable.h
==============================================================================
--- branches/trac347/src/lib/datasrc/zonetable.h (original)
+++ branches/trac347/src/lib/datasrc/zonetable.h Mon Dec 27 11:50:02 2010
@@ -17,6 +17,10 @@
#include <boost/shared_ptr.hpp>
+#include <dns/rrset.h>
+
+#include <datasrc/zone.h>
+
namespace isc {
namespace dns {
class Name;
@@ -25,144 +29,24 @@
namespace datasrc {
-/// \brief A single authoritative zone
-///
-/// The \c Zone class represents a DNS zone as part of %data source.
-///
-/// At the moment this is provided mainly for making the \c ZoneTable class
-/// testable, and only provides a minimal set of features.
-/// This is why this class is defined in the same header file, but it may
-/// have to move to a separate header file when we understand what is
-/// necessary for this class for actual operation.
-/// Likewise, it will have more features. For example, it will maintain
-/// information about the location of a zone file, whether it's loaded in
-/// memory, etc.
-class Zone {
- ///
- /// \name Constructors and Destructor.
- ///
- /// \b Note:
- /// The copy constructor and the assignment operator are intentionally
- /// defined as private, making this class non copyable.
- //@{
-private:
- Zone(const Zone& source);
- Zone& operator=(const Zone& source);
-public:
- /// \brief Constructor from zone parameters.
- ///
- /// This constructor internally involves resource allocation, and if
- /// it fails, a corresponding standard exception will be thrown.
- /// It never throws an exception otherwise.
- ///
- /// \param rrclass The RR class of the zone.
- /// \param origin The origin name of the zone.
- Zone(const isc::dns::RRClass& rrclass, const isc::dns::Name& origin);
-
- /// The destructor.
- ~Zone();
- //@}
-
- ///
- /// \name Getter Methods
- ///
- /// These methods never throw an exception.
- //@{
- /// \brief Return the origin name of the zone.
- const isc::dns::Name& getOrigin() const;
-
- /// \brief Return the RR class of the zone.
- const isc::dns::RRClass& getClass() const;
- //@}
-
-private:
- struct ZoneImpl;
- ZoneImpl* impl_;
-};
-
-/// \brief A pointer-like type pointing to a \c Zone object.
-typedef boost::shared_ptr<Zone> ZonePtr;
-
-/// \brief A pointer-like type pointing to a \c Zone object.
-typedef boost::shared_ptr<const Zone> ConstZonePtr;
-
/// \brief A set of authoritative zones.
///
-/// The \c ZoneTable class represents a set of zones of the same RR class
-/// and provides a basic interface to help DNS lookup processing.
-/// For a given domain name, its \c find() method searches the set for a zone
-/// that gives a longest match against that name.
+/// \c ZoneTable class is primarily intended to be used as a backend for the
+/// \c MemoryDataSrc class, but is exposed as a separate class in case some
+/// application wants to use it directly (e.g. for a customized data source
+/// implementation).
///
-/// The set of zones are assumed to be of the same RR class, but the
-/// \c ZoneTable class does not enforce the assumption through its interface.
-/// For example, the \c add() method does not check if the new zone
-/// is of the same RR class as that of the others already in the table.
-/// It is caller's responsibility to ensure this assumption.
-///
-/// <b>Notes to developer:</b>
-///
-/// The add() method takes a (Boost) shared pointer because it would be
-/// inconvenient to require the caller to maintain the ownership of zones,
-/// while it wouldn't be safe to delete unnecessary zones inside the zone
-/// table.
-///
-/// On the other hand, the find() method returns a bare pointer, rather than
-/// the shared pointer, in order to minimize the dependency on Boost
-/// definitions in our public interfaces. This means the caller can only
-/// refer to the returned object (via the pointer) for a short period.
-/// It should be okay for simple lookup purposes, but if we see the need
-/// for keeping a \c Zone object for a longer period of context, we may
-/// have to revisit this decision.
-///
-/// Currently, \c FindResult::zone is immutable for safety.
-/// In future versions we may want to make it changeable. For example,
-/// we may want to allow configuration update on an existing zone.
-///
-/// In BIND 9's "zt" module, the equivalent of \c find() has an "option"
-/// parameter. The only defined option is the one to specify the "no exact"
-/// mode, and the only purpose of that mode is to prefer a second longest match
-/// even if there is an exact match in order to deal with type DS query.
-/// This trick may help enhance performance, but it also seems to make the
-/// implementation complicated for a very limited, minor case. So, for now,
-/// we don't introduce the special mode, and, since it was the only reason to
-/// have search options in BIND 9, our initial implementation doesn't provide
-/// a switch for options.
+/// For more descriptions about its struct and interfaces, please refer to the
+/// corresponding struct and interfaces of \c MemoryDataSrc.
class ZoneTable {
public:
- /// Result codes of various public methods of \c ZoneTable.
- ///
- /// The detailed semantics may differ in different methods.
- /// See the description of specific methods for more details.
- enum Result {
- SUCCESS, ///< The operation is successful.
- EXIST, ///< A zone is already stored in \c ZoneTable.
- NOTFOUND, ///< The specified zone is not found in \c ZoneTable.
- PARTIALMATCH ///< \c Only a partial match is found in \c find().
- };
-
- /// \brief A helper structure to represent the search result of
- /// <code>ZoneTable::find()</code>.
- ///
- /// This is a straightforward pair of the result code and a pointer
- /// to the found zone to represent the result of \c find().
- /// We use this in order to avoid overloading the return value for both
- /// the result code ("success" or "not found") and the found object,
- /// i.e., avoid using \c NULL to mean "not found", etc.
- ///
- /// This is a simple value class with no internal state, so for
- /// convenience we allow the applications to refer to the members
- /// directly.
- ///
- /// See the description of \c find() for the semantics of the member
- /// variables.
struct FindResult {
- FindResult(Result param_code, const Zone* param_zone) :
+ FindResult(result::Result param_code, const ConstZonePtr param_zone) :
code(param_code), zone(param_zone)
{}
- const Result code;
- const Zone* const zone;
+ const result::Result code;
+ const ConstZonePtr zone;
};
-
///
/// \name Constructors and Destructor.
///
@@ -188,28 +72,29 @@
/// Add a \c Zone to the \c ZoneTable.
///
- /// \c zone must not be associated with a NULL pointer; otherwise
+ /// \c Zone must not be associated with a NULL pointer; otherwise
/// an exception of class \c InvalidParameter will be thrown.
/// If internal resource allocation fails, a corresponding standard
/// exception will be thrown.
/// This method never throws an exception otherwise.
///
/// \param zone A \c Zone object to be added.
- /// \return \c SUCCESS If the zone is successfully added to the zone table.
- /// \return \c EXIST The zone table already stores a zone that has the
- /// same origin.
- Result add(ZonePtr zone);
+ /// \return \c result::SUCCESS If the zone is successfully
+ /// added to the zone table.
+ /// \return \c result::EXIST The zone table already contains
+ /// zone of the same origin.
+ result::Result addZone(ZonePtr zone);
/// Remove a \c Zone of the given origin name from the \c ZoneTable.
///
/// This method never throws an exception.
///
/// \param origin The origin name of the zone to be removed.
- /// \return \c SUCCESS If the zone is successfully removed from the
- /// zone table.
- /// \return \c NOTFOUND The zone table does not store the zone that matches
- /// \c origin.
- Result remove(const isc::dns::Name& origin);
+ /// \return \c result::SUCCESS If the zone is successfully
+ /// removed from the zone table.
+ /// \return \c result::NOTFOUND The zone table does not
+ /// store the zone that matches \c origin.
+ result::Result removeZone(const isc::dns::Name& origin);
/// Find a \c Zone that best matches the given name in the \c ZoneTable.
///
@@ -217,23 +102,19 @@
/// longest match against \c name, and returns the result in the
/// form of a \c FindResult object as follows:
/// - \c code: The result code of the operation.
- /// - \c SUCCESS: A zone that gives an exact match is found
- /// - \c PARTIALMATCH: A zone whose origin is a super domain of
- /// \c name is found (but there is no exact match)
- /// - \c NOTFOUND: For all other cases.
- /// - \c zone: A pointer to the found \c Zone object if one is found;
- /// otherwise \c NULL.
- ///
- /// The pointer returned in the \c FindResult object is only valid until
- /// the corresponding zone is removed from the zone table.
- /// The caller must ensure that the zone is held in the zone table while
- /// it needs to refer to it.
+ /// - \c result::SUCCESS: A zone that gives an exact match
+ /// is found
+ /// - \c result::PARTIALMATCH: A zone whose origin is a
+ /// super domain of \c name is found (but there is no exact match)
+ /// - \c result::NOTFOUND: For all other cases.
+ /// - \c zone: A <Boost> shared pointer to the found \c Zone object if one
+ /// is found; otherwise \c NULL.
///
/// This method never throws an exception.
///
/// \param name A domain name for which the search is performed.
/// \return A \c FindResult object enclosing the search result (see above).
- FindResult find(const isc::dns::Name& name) const;
+ FindResult findZone(const isc::dns::Name& name) const;
private:
struct ZoneTableImpl;
Modified: branches/trac347/src/lib/dns/Makefile.am
==============================================================================
--- branches/trac347/src/lib/dns/Makefile.am (original)
+++ branches/trac347/src/lib/dns/Makefile.am Mon Dec 27 11:50:02 2010
@@ -69,6 +69,7 @@
libdns___la_SOURCES += edns.h edns.cc
libdns___la_SOURCES += exceptions.h exceptions.cc
libdns___la_SOURCES += util/hex.h
+libdns___la_SOURCES += masterload.h masterload.cc
libdns___la_SOURCES += message.h message.cc
libdns___la_SOURCES += messagerenderer.h messagerenderer.cc
libdns___la_SOURCES += name.h name.cc
Modified: branches/trac347/src/lib/dns/buffer.h
==============================================================================
--- branches/trac347/src/lib/dns/buffer.h (original)
+++ branches/trac347/src/lib/dns/buffer.h Mon Dec 27 11:50:02 2010
@@ -25,6 +25,8 @@
#include <exceptions/exceptions.h>
+#include <boost/shared_ptr.hpp>
+
namespace isc {
namespace dns {
@@ -412,6 +414,16 @@
private:
std::vector<uint8_t> data_;
};
+
+/// \brief Pointer-like types pointing to \c InputBuffer or \c OutputBuffer
+///
+/// These types are expected to be used as an argument in asynchronous
+/// callback functions. The internal reference-counting will ensure that
+/// that ongoing state information will not be lost if the object
+/// that originated the asynchronous call falls out of scope.
+typedef boost::shared_ptr<InputBuffer> InputBufferPtr;
+typedef boost::shared_ptr<OutputBuffer> OutputBufferPtr;
+
}
}
#endif // __BUFFER_H
Modified: branches/trac347/src/lib/dns/message.h
==============================================================================
--- branches/trac347/src/lib/dns/message.h (original)
+++ branches/trac347/src/lib/dns/message.h Mon Dec 27 11:50:02 2010
@@ -517,6 +517,14 @@
MessageImpl* impl_;
};
+/// \brief Pointer-like type pointing to a \c Message
+///
+/// This type is expected to be used as an argument in asynchronous
+/// callback functions. The internal reference-counting will ensure that
+/// that ongoing state information will not be lost if the object
+/// that originated the asynchronous call falls out of scope.
+typedef boost::shared_ptr<Message> MessagePtr;
+
std::ostream& operator<<(std::ostream& os, const Message& message);
}
}
Modified: branches/trac347/src/lib/dns/messagerenderer.h
==============================================================================
--- branches/trac347/src/lib/dns/messagerenderer.h (original)
+++ branches/trac347/src/lib/dns/messagerenderer.h Mon Dec 27 11:50:02 2010
@@ -258,6 +258,7 @@
/// \param name A \c Name object to be written.
/// \param compress A boolean indicating whether to enable name compression.
void writeName(const Name& name, bool compress = true);
+ //@}
private:
struct MessageRendererImpl;
MessageRendererImpl* impl_;
Modified: branches/trac347/src/lib/dns/tests/Makefile.am
==============================================================================
--- branches/trac347/src/lib/dns/tests/Makefile.am (original)
+++ branches/trac347/src/lib/dns/tests/Makefile.am Mon Dec 27 11:50:02 2010
@@ -42,6 +42,7 @@
run_unittests_SOURCES += rrset_unittest.cc rrsetlist_unittest.cc
run_unittests_SOURCES += question_unittest.cc
run_unittests_SOURCES += rrparamregistry_unittest.cc
+run_unittests_SOURCES += masterload_unittest.cc
run_unittests_SOURCES += message_unittest.cc
run_unittests_SOURCES += base32hex_unittest.cc
run_unittests_SOURCES += base64_unittest.cc
Modified: branches/trac347/src/lib/dns/tests/testdata/Makefile.am
==============================================================================
--- branches/trac347/src/lib/dns/tests/testdata/Makefile.am (original)
+++ branches/trac347/src/lib/dns/tests/testdata/Makefile.am Mon Dec 27 11:50:02 2010
@@ -26,6 +26,7 @@
EXTRA_DIST = gen-wiredata.py.in
EXTRA_DIST += edns_toWire1.spec edns_toWire2.spec
EXTRA_DIST += edns_toWire3.spec edns_toWire4.spec
+EXTRA_DIST += masterload.txt
EXTRA_DIST += message_fromWire1 message_fromWire2
EXTRA_DIST += message_fromWire3 message_fromWire4
EXTRA_DIST += message_fromWire5 message_fromWire6
Modified: branches/trac347/src/lib/dns/tests/testdata/edns_toWire4.spec
==============================================================================
--- branches/trac347/src/lib/dns/tests/testdata/edns_toWire4.spec (original)
+++ branches/trac347/src/lib/dns/tests/testdata/edns_toWire4.spec Mon Dec 27 11:50:02 2010
@@ -1,5 +1,6 @@
#
-# Same as edns_toWire1 but setting the DO bit
+# Same as edns_toWire1 but setting the DO bit, and using an unusual
+# UDP payload size
#
[edns]
do: 1
Modified: branches/trac347/src/lib/dns/tests/tsigkey_unittest.cc
==============================================================================
--- branches/trac347/src/lib/dns/tests/tsigkey_unittest.cc (original)
+++ branches/trac347/src/lib/dns/tests/tsigkey_unittest.cc Mon Dec 27 11:50:02 2010
@@ -11,8 +11,6 @@
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-
-// $Id: rrtype_unittest.cc 476 2010-01-19 00:29:28Z jinmei $
#include <string>
Modified: branches/trac347/src/lib/dns/tests/unittest_util.cc
==============================================================================
--- branches/trac347/src/lib/dns/tests/unittest_util.cc (original)
+++ branches/trac347/src/lib/dns/tests/unittest_util.cc Mon Dec 27 11:50:02 2010
@@ -25,13 +25,15 @@
#include <gtest/gtest.h>
+#include <dns/rcode.h>
#include <dns/name.h>
+#include <dns/message.h>
#include <dns/tests/unittest_util.h>
using namespace std;
+using namespace isc::dns;
using isc::UnitTestUtil;
-using isc::dns::NameComparisonResult;
namespace {
class UnitTestUtilConfig {
@@ -175,3 +177,19 @@
}
return (::testing::AssertionSuccess());
}
+
+void
+UnitTestUtil::createRequestMessage(Message& message,
+ const Opcode& opcode,
+ const uint16_t qid,
+ const Name& name,
+ const RRClass& rrclass,
+ const RRType& rrtype)
+{
+ message.clear(Message::RENDER);
+ message.setOpcode(opcode);
+ message.setRcode(Rcode::NOERROR());
+ message.setQid(qid);
+ message.addQuestion(Question(name, rrclass, rrtype));
+}
+
Modified: branches/trac347/src/lib/dns/tests/unittest_util.h
==============================================================================
--- branches/trac347/src/lib/dns/tests/unittest_util.h (original)
+++ branches/trac347/src/lib/dns/tests/unittest_util.h Mon Dec 27 11:50:02 2010
@@ -21,6 +21,7 @@
#include <string>
#include <dns/name.h>
+#include <dns/message.h>
#include <gtest/gtest.h>
@@ -80,6 +81,20 @@
static ::testing::AssertionResult
matchName(const char* nameexp1, const char* nameexp2,
const isc::dns::Name& name1, const isc::dns::Name& name2);
+
+ ///
+ /// Populate a request message
+ ///
+ /// Create a request message in 'request_message' using the
+ /// opcode 'opcode' and the name/class/type query tuple specified in
+ /// 'name', 'rrclass' and 'rrtype.
+ static void
+ createRequestMessage(isc::dns::Message& request_message,
+ const isc::dns::Opcode& opcode,
+ const uint16_t qid,
+ const isc::dns::Name& name,
+ const isc::dns::RRClass& rrclass,
+ const isc::dns::RRType& rrtype);
};
}
#endif // __UNITTEST_UTIL_H
Modified: branches/trac347/src/lib/python/isc/cc/data.py
==============================================================================
--- branches/trac347/src/lib/python/isc/cc/data.py (original)
+++ branches/trac347/src/lib/python/isc/cc/data.py Mon Dec 27 11:50:02 2010
@@ -56,20 +56,116 @@
for k in null_keys:
del d[k]
+def _concat_identifier(id_parts):
+ """Concatenates the given identifier parts into a string,
+ delimited with the '/' character.
+ """
+ return '/'.join(id_parts)
+
+def split_identifier(identifier):
+ """Splits the given identifier into a list of identifier parts,
+ as delimited by the '/' character.
+ Raises a DataTypeError if identifier is not a string."""
+ if type(identifier) != str:
+ raise DataTypeError("identifier is not a string")
+ id_parts = identifier.split('/')
+ id_parts[:] = (value for value in id_parts if value != "")
+ return id_parts
+
+def split_identifier_list_indices(identifier):
+ """Finds list indexes in the given identifier, which are of the
+ format [integer].
+ Identifier must be a string.
+ This will only give the list index for the last 'part' of the
+ given identifier (as delimited by the '/' sign).
+ Raises a DataTypeError if the identifier is not a string,
+ or if the format is bad.
+ Returns a tuple, where the first element is the string part of
+ the identifier, and the second element is a list of (nested) list
+ indices.
+ Examples:
+ 'a/b/c' will return ('a/b/c', None)
+ 'a/b/c[1]' will return ('a/b/c', [1])
+ 'a/b/c[1][2][3]' will return ('a/b/c', [1, 2, 3])
+ 'a[0]/b[1]/c[2]' will return ('a[0]/b[1]/c', [2])
+ """
+ if type(identifier) != str:
+ raise DataTypeError("identifier in "
+ "split_identifier_list_indices() "
+ "not a string: " + str(identifier))
+
+ # We only work on the final 'part' of the identifier
+ id_parts = split_identifier(identifier)
+ id_str = id_parts[-1]
+
+ i = id_str.find('[')
+ if i < 0:
+ if identifier.find(']') >= 0:
+ raise DataTypeError("Bad format in identifier: " + str(identifier))
+ return identifier, None
+
+ # keep the non-index part of that to replace later
+ id = id_str[:i]
+ indices = []
+ while i >= 0:
+ e = id_str.find(']')
+ if e < i + 1:
+ raise DataTypeError("Bad format in identifier: " + str(identifier))
+ try:
+ indices.append(int(id_str[i+1:e]))
+ except ValueError:
+ raise DataTypeError("List index in " + identifier + " not an integer")
+ id_str = id_str[e + 1:]
+ i = id_str.find('[')
+ if i > 0:
+ raise DataTypeError("Bad format in identifier: " + str(identifier))
+ if id.find(']') >= 0 or len(id_str) > 0:
+ raise DataTypeError("Bad format in identifier: " + str(identifier))
+
+ # we replace the final part of the original identifier with
+ # the stripped string
+ id_parts[-1] = id
+ id = _concat_identifier(id_parts)
+ return id, indices
+
+def _find_child_el(element, id):
+ """Finds the child of element with the given id. If the id contains
+ [i], where i is a number, and the child element is a list, the
+ i-th element of that list is returned instead of the list itself.
+ Raises a DataTypeError if the element is of wrong type, if id
+ is not a string, or if the id string contains a bad value.
+ Raises a DataNotFoundError if the element at id could not be
+ found.
+ """
+ id, list_indices = split_identifier_list_indices(id)
+ if type(element) == dict and id in element.keys():
+ result = element[id]
+ else:
+ raise DataNotFoundError(id + " in " + str(element))
+ if type(result) == list and list_indices is not None:
+ for list_index in list_indices:
+ if list_index >= len(result):
+ raise DataNotFoundError("Element " + str(list_index) + " in " + str(result))
+ result = result[list_index]
+ return result
+
def find(element, identifier):
- """Returns the subelement in the given data element, raises DataNotFoundError if not found"""
- if type(identifier) != str or (type(element) != dict and identifier != ""):
- raise DataTypeError("identifier in merge() is not a string")
- if type(identifier) != str or (type(element) != dict and identifier != ""):
- raise DataTypeError("element in merge() is not a dict")
- id_parts = identifier.split("/")
- id_parts[:] = (value for value in id_parts if value != "")
+ """Returns the subelement in the given data element, raises
+ DataNotFoundError if not found.
+ Returns the given element if the identifier is an empty string.
+ Raises a DataTypeError if identifier is not a string, or if
+ identifier is not empty, and element is not a dict.
+ """
+ if type(identifier) != str:
+ raise DataTypeError("identifier in find() is not a str")
+ if identifier == "":
+ return element
+ if type(element) != dict:
+ raise DataTypeError("element in find() is not a dict")
+ id_parts = split_identifier(identifier)
cur_el = element
for id in id_parts:
- if type(cur_el) == dict and id in cur_el.keys():
- cur_el = cur_el[id]
- else:
- raise DataNotFoundError(identifier + " in " + str(element))
+ cur_el = _find_child_el(cur_el, id)
return cur_el
def set(element, identifier, value):
@@ -83,25 +179,46 @@
if type(element) != dict:
raise DataTypeError("element in set() is not a dict")
if type(identifier) != str:
- raise DataTypeError("identifier in set() is not a string")
- id_parts = identifier.split("/")
- id_parts[:] = (value for value in id_parts if value != "")
+ raise DataTypeError("identifier in set() is not a str")
+ id_parts = split_identifier(identifier)
cur_el = element
for id in id_parts[:-1]:
- if id in cur_el.keys():
- cur_el = cur_el[id]
- else:
- if value == None:
+ try:
+ cur_el = _find_child_el(cur_el, id)
+ except DataNotFoundError:
+ if value is None:
# ok we are unsetting a value that wasn't set in
# the first place. Simply stop.
return
cur_el[id] = {}
cur_el = cur_el[id]
- # value can be an empty list or dict, so check for None eplicitely
- if value != None:
- cur_el[id_parts[-1]] = value
- elif id_parts[-1] in cur_el:
- del cur_el[id_parts[-1]]
+
+ id, list_indices = split_identifier_list_indices(id_parts[-1])
+ if list_indices is None:
+ # value can be an empty list or dict, so check for None eplicitely
+ if value is not None:
+ cur_el[id] = value
+ else:
+ del cur_el[id]
+ else:
+ cur_el = cur_el[id]
+ # in case of nested lists, we need to get to the next to last
+ for list_index in list_indices[:-1]:
+ if type(cur_el) != list:
+ raise DataTypeError("Element at " + identifier + " is not a list")
+ if len(cur_el) <= list_index:
+ raise DataNotFoundError("List index at " + identifier + " out of range")
+ cur_el = cur_el[list_index]
+ # value can be an empty list or dict, so check for None eplicitely
+ list_index = list_indices[-1]
+ if type(cur_el) != list:
+ raise DataTypeError("Element at " + identifier + " is not a list")
+ if len(cur_el) <= list_index:
+ raise DataNotFoundError("List index at " + identifier + " out of range")
+ if value is not None:
+ cur_el[list_index] = value
+ else:
+ del cur_el[list_index]
return element
def unset(element, identifier):
@@ -116,17 +233,12 @@
"""Returns the subelement in the given data element, returns None
if not found, or if an error occurred (i.e. this function should
never raise an exception)"""
- if type(identifier) != str:
+ try:
+ return find(element, identifier)
+ except DataNotFoundError:
return None
- id_parts = identifier.split("/")
- id_parts[:] = (value for value in id_parts if value != "")
- cur_el = element
- for id in id_parts:
- if (type(cur_el) == dict and id in cur_el.keys()) or id=="":
- cur_el = cur_el[id]
- else:
- return None
- return cur_el
+ except DataTypeError:
+ return None
def parse_value_str(value_str):
"""Parses the given string to a native python object. If the
@@ -139,7 +251,4 @@
except ValueError as ve:
# simply return the string itself
return value_str
- except SyntaxError as ve:
- # simply return the string itself
- return value_str
-
+
Modified: branches/trac347/src/lib/python/isc/cc/tests/data_test.py
==============================================================================
--- branches/trac347/src/lib/python/isc/cc/tests/data_test.py (original)
+++ branches/trac347/src/lib/python/isc/cc/tests/data_test.py Mon Dec 27 11:50:02 2010
@@ -70,6 +70,11 @@
c = { "a": { "b": "c" } }
data.remove_identical(a, b)
self.assertEqual(a, c)
+
+ self.assertRaises(data.DataTypeError, data.remove_identical,
+ a, 1)
+ self.assertRaises(data.DataTypeError, data.remove_identical,
+ 1, b)
def test_merge(self):
d1 = { 'a': 'a', 'b': 1, 'c': { 'd': 'd', 'e': 2 } }
@@ -81,6 +86,45 @@
self.assertRaises(data.DataTypeError, data.merge, d1, "a")
self.assertRaises(data.DataTypeError, data.merge, 1, d2)
self.assertRaises(data.DataTypeError, data.merge, None, None)
+
+
+ def test_split_identifier_list_indices(self):
+ id, indices = data.split_identifier_list_indices('a')
+ self.assertEqual(id, 'a')
+ self.assertEqual(indices, None)
+ id, indices = data.split_identifier_list_indices('a[0]')
+ self.assertEqual(id, 'a')
+ self.assertEqual(indices, [0])
+ id, indices = data.split_identifier_list_indices('a[0][1]')
+ self.assertEqual(id, 'a')
+ self.assertEqual(indices, [0, 1])
+
+ # examples from the docstring
+ id, indices = data.split_identifier_list_indices('a/b/c')
+ self.assertEqual(id, 'a/b/c')
+ self.assertEqual(indices, None)
+
+ id, indices = data.split_identifier_list_indices('a/b/c[1]')
+ self.assertEqual(id, 'a/b/c')
+ self.assertEqual(indices, [1])
+
+ id, indices = data.split_identifier_list_indices('a/b/c[1][2][3]')
+ self.assertEqual(id, 'a/b/c')
+ self.assertEqual(indices, [1, 2, 3])
+
+ id, indices = data.split_identifier_list_indices('a[0]/b[1]/c[2]')
+ self.assertEqual(id, 'a[0]/b[1]/c')
+ self.assertEqual(indices, [2])
+
+ # bad formats
+ self.assertRaises(data.DataTypeError, data.split_identifier_list_indices, 'a[')
+ self.assertRaises(data.DataTypeError, data.split_identifier_list_indices, 'a]')
+ self.assertRaises(data.DataTypeError, data.split_identifier_list_indices, 'a[[0]]')
+ self.assertRaises(data.DataTypeError, data.split_identifier_list_indices, 'a[0]a')
+ self.assertRaises(data.DataTypeError, data.split_identifier_list_indices, 'a[0]a[1]')
+
+ self.assertRaises(data.DataTypeError, data.split_identifier_list_indices, 1)
+
def test_find(self):
d1 = { 'a': 'a', 'b': 1, 'c': { 'd': 'd', 'e': 2, 'more': { 'data': 'here' } } }
@@ -93,19 +137,47 @@
self.assertRaises(data.DataNotFoundError, data.find, d1, 'f')
self.assertRaises(data.DataTypeError, data.find, d1, 1)
self.assertRaises(data.DataTypeError, data.find, None, 1)
+ self.assertRaises(data.DataTypeError, data.find, None, "foo")
self.assertRaises(data.DataTypeError, data.find, "123", "123")
self.assertEqual(data.find("123", ""), "123")
+
+ d2 = { 'a': [ 1, 2, 3 ] }
+ self.assertEqual(data.find(d2, 'a[0]'), 1)
+ self.assertEqual(data.find(d2, 'a[1]'), 2)
+ self.assertEqual(data.find(d2, 'a[2]'), 3)
+ self.assertRaises(data.DataNotFoundError, data.find, d2, 'a[3]')
+ self.assertRaises(data.DataTypeError, data.find, d2, 'a[a]')
+
+ d3 = { 'a': [ { 'b': [ {}, { 'c': 'd' } ] } ] }
+ self.assertEqual(data.find(d3, 'a[0]/b[1]/c'), 'd')
+ self.assertRaises(data.DataNotFoundError, data.find, d3, 'a[1]/b[1]/c')
def test_set(self):
d1 = { 'a': 'a', 'b': 1, 'c': { 'd': 'd', 'e': 2 } }
d12 = { 'b': 1, 'c': { 'e': 3, 'f': [ 1 ] } }
+ d13 = { 'b': 1, 'c': { 'e': 3, 'f': [ 2 ] } }
+ d14 = { 'b': 1, 'c': { 'e': 3, 'f': [ { 'g': [ 1, 2 ] } ] } }
+ d15 = { 'b': 1, 'c': { 'e': 3, 'f': [ { 'g': [ 1, 3 ] } ] } }
data.set(d1, 'a', None)
data.set(d1, 'c/d', None)
data.set(d1, 'c/e/', 3)
data.set(d1, 'c/f', [ 1 ] )
self.assertEqual(d1, d12)
+ data.set(d1, 'c/f[0]', 2 )
+ self.assertEqual(d1, d13)
+
+ data.set(d1, 'c/f[0]', { 'g': [ 1, 2] } )
+ self.assertEqual(d1, d14)
+ data.set(d1, 'c/f[0]/g[1]', 3)
+ self.assertEqual(d1, d15)
+
self.assertRaises(data.DataTypeError, data.set, d1, 1, 2)
self.assertRaises(data.DataTypeError, data.set, 1, "", 2)
+ self.assertRaises(data.DataTypeError, data.set, d1, 'c[1]', 2)
+ self.assertRaises(data.DataTypeError, data.set, d1, 'c[1][2]', 2)
+ self.assertRaises(data.DataNotFoundError, data.set, d1, 'c/f[5]', 2)
+ self.assertRaises(data.DataNotFoundError, data.set, d1, 'c/f[5][2]', 2)
+
d3 = {}
e3 = data.set(d3, "does/not/exist", 123)
self.assertEqual(d3,
@@ -114,11 +186,25 @@
{ 'does': { 'not': { 'exist': 123 } } })
def test_unset(self):
- d1 = { 'a': 'a', 'b': 1, 'c': { 'd': 'd', 'e': 2 } }
+ d1 = { 'a': 'a', 'b': 1, 'c': { 'd': 'd', 'e': [ 1, 2, 3 ] } }
data.unset(d1, 'a')
data.unset(d1, 'c/d')
data.unset(d1, 'does/not/exist')
- self.assertEqual(d1, { 'b': 1, 'c': { 'e': 2 } })
+ self.assertEqual(d1, { 'b': 1, 'c': { 'e': [ 1, 2, 3 ] } })
+ data.unset(d1, 'c/e[0]')
+ self.assertEqual(d1, { 'b': 1, 'c': { 'e': [ 2, 3 ] } })
+ data.unset(d1, 'c/e[1]')
+ self.assertEqual(d1, { 'b': 1, 'c': { 'e': [ 2 ] } })
+ # index 1 should now be out of range
+ self.assertRaises(data.DataNotFoundError, data.unset, d1, 'c/e[1]')
+ d2 = { 'a': [ { 'b': [ 1, 2 ] } ] }
+ data.unset(d2, 'a[0]/b[1]')
+ self.assertEqual(d2, { 'a': [ { 'b': [ 1 ] } ] })
+ d3 = { 'a': [ [ 1, 2 ] ] }
+ data.set(d3, "a[0][1]", 3)
+ self.assertEqual(d3, { 'a': [ [ 1, 3 ] ] })
+ data.unset(d3, 'a[0][1]')
+ self.assertEqual(d3, { 'a': [ [ 1 ] ] })
def test_find_no_exc(self):
d1 = { 'a': 'a', 'b': 1, 'c': { 'd': 'd', 'e': 2, 'more': { 'data': 'here' } } }
@@ -146,6 +232,9 @@
self.assertEqual(data.parse_value_str("{ \"a\": \"b\", \"c\": 1 }"), { 'a': 'b', 'c': 1 })
self.assertEqual(data.parse_value_str("[ a c"), "[ a c")
+ self.assertEqual(data.parse_value_str(1), None)
+
+
if __name__ == '__main__':
#if not 'CONFIG_TESTDATA_PATH' in os.environ:
# print("You need to set the environment variable CONFIG_TESTDATA_PATH to point to the directory containing the test data files")
Modified: branches/trac347/src/lib/python/isc/config/ccsession.py
==============================================================================
--- branches/trac347/src/lib/python/isc/config/ccsession.py (original)
+++ branches/trac347/src/lib/python/isc/config/ccsession.py Mon Dec 27 11:50:02 2010
@@ -224,7 +224,7 @@
if not self._config_handler:
answer = create_answer(2, self._module_name + " has no config handler")
elif not self.get_module_spec().validate_config(False, new_config, errors):
- answer = create_answer(1, " ".join(errors))
+ answer = create_answer(1, ", ".join(errors))
else:
isc.cc.data.remove_identical(new_config, self.get_local_config())
answer = self._config_handler(new_config)
@@ -398,22 +398,40 @@
module_spec = self.find_spec_part(identifier)
if (type(module_spec) != dict or "list_item_spec" not in module_spec):
raise isc.cc.data.DataNotFoundError(str(identifier) + " is not a list")
- value = isc.cc.data.parse_value_str(value_str)
- isc.config.config_data.check_type(module_spec, [value])
- cur_list, status = self.get_value(identifier)
- #if not cur_list:
- # cur_list = isc.cc.data.find_no_exc(self.config.data, identifier)
- if not cur_list:
- cur_list = []
- if value in cur_list:
- cur_list.remove(value)
- self.set_value(identifier, cur_list)
+
+ if value_str is None:
+ # we are directly removing an list index
+ id, list_indices = isc.cc.data.split_identifier_list_indices(identifier)
+ if list_indices is None:
+ raise DataTypeError("identifier in remove_value() does not contain a list index, and no value to remove")
+ else:
+ self.set_value(identifier, None)
+ else:
+ value = isc.cc.data.parse_value_str(value_str)
+ isc.config.config_data.check_type(module_spec, [value])
+ cur_list, status = self.get_value(identifier)
+ #if not cur_list:
+ # cur_list = isc.cc.data.find_no_exc(self.config.data, identifier)
+ if not cur_list:
+ cur_list = []
+ if value in cur_list:
+ cur_list.remove(value)
+ self.set_value(identifier, cur_list)
def commit(self):
"""Commit all local changes, send them through b10-cmdctl to
the configuration manager"""
if self.get_local_changes():
- self._conn.send_POST('/ConfigManager/set_config', [ self.get_local_changes() ])
- # todo: check result
- self.request_current_config()
- self.clear_local_changes()
+ response = self._conn.send_POST('/ConfigManager/set_config',
+ [ self.get_local_changes() ])
+ answer = isc.cc.data.parse_value_str(response.read().decode())
+ # answer is either an empty dict (on success), or one
+ # containing errors
+ if answer == {}:
+ self.request_current_config()
+ self.clear_local_changes()
+ elif "error" in answer:
+ print("Error: " + answer["error"])
+ print("Configuration not committed")
+ else:
+ raise ModuleCCSessionError("Unknown format of answer in commit(): " + str(answer))
Modified: branches/trac347/src/lib/python/isc/config/config_data.py
==============================================================================
--- branches/trac347/src/lib/python/isc/config/config_data.py (original)
+++ branches/trac347/src/lib/python/isc/config/config_data.py Mon Dec 27 11:50:02 2010
@@ -22,6 +22,7 @@
import isc.cc.data
import isc.config.module_spec
+import ast
class ConfigDataError(Exception): pass
@@ -56,14 +57,14 @@
raise isc.cc.data.DataTypeError(str(value) + " is not a map")
def convert_type(spec_part, value):
- """Convert the give value(type is string) according specification
+ """Convert the given value(type is string) according specification
part relevant for the value. Raises an isc.cc.data.DataTypeError
exception if conversion failed.
"""
if type(spec_part) == dict and 'item_type' in spec_part:
data_type = spec_part['item_type']
else:
- raise isc.cc.data.DataTypeError(str("Incorrect specification part for type convering"))
+ raise isc.cc.data.DataTypeError(str("Incorrect specification part for type conversion"))
try:
if data_type == "integer":
@@ -81,18 +82,25 @@
ret.append(convert_type(spec_part['list_item_spec'], item))
elif type(value) == str:
value = value.split(',')
- for item in value:
+ for item in value:
sub_value = item.split()
for sub_item in sub_value:
- ret.append(convert_type(spec_part['list_item_spec'], sub_item))
+ ret.append(convert_type(spec_part['list_item_spec'],
+ sub_item))
if ret == []:
raise isc.cc.data.DataTypeError(str(value) + " is not a list")
return ret
elif data_type == "map":
- return dict(value)
- # todo: check types of map contents too
+ map = ast.literal_eval(value)
+ if type(map) == dict:
+ # todo: check types of map contents too
+ return map
+ else:
+ raise isc.cc.data.DataTypeError(
+ "Value in convert_type not a string "
+ "specifying a dict")
else:
return value
except ValueError as err:
@@ -108,7 +116,11 @@
id_parts = identifier.split("/")
id_parts[:] = (value for value in id_parts if value != "")
cur_el = element
- for id in id_parts:
+
+ for id_part in id_parts:
+ # strip list selector part
+ # don't need it for the spec part, so just drop it
+ id, list_indices = isc.cc.data.split_identifier_list_indices(id_part)
if type(cur_el) == dict and 'map_item_spec' in cur_el.keys():
found = False
for cur_el_item in cur_el['map_item_spec']:
@@ -226,6 +238,20 @@
result[item] = value
return result
+# should we just make a class for these?
+def _create_value_map_entry(name, type, value, status = None):
+ entry = {}
+ entry['name'] = name
+ entry['type'] = type
+ entry['value'] = value
+ entry['modified'] = False
+ entry['default'] = False
+ if status == MultiConfigData.LOCAL:
+ entry['modified'] = True
+ if status == MultiConfigData.DEFAULT:
+ entry['default'] = True
+ return entry
+
class MultiConfigData:
"""This class stores the module specs, current non-default
configuration values and 'local' (uncommitted) changes for
@@ -270,7 +296,7 @@
identifier (up to the first /) is interpreted as the module
name. Returns None if not found, or if identifier is not a
string."""
- if type(identifier) != str:
+ if type(identifier) != str or identifier == "":
return None
if identifier[0] == '/':
identifier = identifier[1:]
@@ -334,28 +360,42 @@
try:
spec = find_spec_part(self._specifications[module].get_config_spec(), id)
if 'item_default' in spec:
- return spec['item_default']
+ id, list_indices = isc.cc.data.split_identifier_list_indices(id)
+ if list_indices is not None and \
+ type(spec['item_default']) == list:
+ if len(list_indices) == 1:
+ default_list = spec['item_default']
+ index = list_indices[0]
+ if index < len(default_list):
+ return default_list[index]
+ else:
+ return None
+ else:
+ return spec['item_default']
else:
return None
except isc.cc.data.DataNotFoundError as dnfe:
return None
- def get_value(self, identifier):
+ def get_value(self, identifier, default = True):
"""Returns a tuple containing value,status.
The value contains the configuration value for the given
identifier. The status reports where this value came from;
it is one of: LOCAL, CURRENT, DEFAULT or NONE, corresponding
(local change, current setting, default as specified by the
- specification, or not found at all)."""
+ specification, or not found at all). Does not check and
+ set DEFAULT if the argument 'default' is False (default
+ defaults to True)"""
value = self.get_local_value(identifier)
if value != None:
return value, self.LOCAL
value = self.get_current_value(identifier)
if value != None:
return value, self.CURRENT
- value = self.get_default_value(identifier)
- if value != None:
- return value, self.DEFAULT
+ if default:
+ value = self.get_default_value(identifier)
+ if value != None:
+ return value, self.DEFAULT
return None, self.NONE
def get_value_maps(self, identifier = None):
@@ -372,12 +412,7 @@
if not identifier:
# No identifier, so we need the list of current modules
for module in self._specifications.keys():
- entry = {}
- entry['name'] = module
- entry['type'] = 'module'
- entry['value'] = None
- entry['modified'] = False
- entry['default'] = False
+ entry = _create_value_map_entry(module, 'module', None)
result.append(entry)
else:
if identifier[0] == '/':
@@ -387,51 +422,41 @@
if spec:
spec_part = find_spec_part(spec.get_config_spec(), id)
if type(spec_part) == list:
+ # list of items to show
for item in spec_part:
- entry = {}
- entry['name'] = item['item_name']
- entry['type'] = item['item_type']
- value, status = self.get_value("/" + identifier + "/" + item['item_name'])
- entry['value'] = value
- if status == self.LOCAL:
- entry['modified'] = True
- else:
- entry['modified'] = False
- if status == self.DEFAULT:
- entry['default'] = False
- else:
- entry['default'] = False
+ value, status = self.get_value("/" + identifier\
+ + "/" + item['item_name'])
+ entry = _create_value_map_entry(item['item_name'],
+ item['item_type'],
+ value, status)
result.append(entry)
elif type(spec_part) == dict:
+ # Sub-specification
item = spec_part
if item['item_type'] == 'list':
li_spec = item['list_item_spec']
- item_list, status = self.get_value("/" + identifier)
- if item_list != None:
- for value in item_list:
- result_part2 = {}
- result_part2['name'] = li_spec['item_name']
- result_part2['value'] = value
- result_part2['type'] = li_spec['item_type']
- result_part2['default'] = False
- result_part2['modified'] = False
+ value, status = self.get_value("/" + identifier)
+ if type(value) == list:
+ for list_value in value:
+ result_part2 = _create_value_map_entry(
+ li_spec['item_name'],
+ li_spec['item_type'],
+ list_value)
result.append(result_part2)
+ elif value is not None:
+ entry = _create_value_map_entry(
+ li_spec['item_name'],
+ li_spec['item_type'],
+ value, status)
+ result.append(entry)
else:
- entry = {}
- entry['name'] = item['item_name']
- entry['type'] = item['item_type']
- #value, status = self.get_value("/" + identifier + "/" + item['item_name'])
value, status = self.get_value("/" + identifier)
- entry['value'] = value
- if status == self.LOCAL:
- entry['modified'] = True
- else:
- entry['modified'] = False
- if status == self.DEFAULT:
- entry['default'] = False
- else:
- entry['default'] = False
- result.append(entry)
+ if value is not None:
+ entry = _create_value_map_entry(
+ item['item_name'],
+ item['item_type'],
+ value, status)
+ result.append(entry)
return result
def set_value(self, identifier, value):
@@ -439,8 +464,31 @@
there is a specification for the given identifier, the type
is checked."""
spec_part = self.find_spec_part(identifier)
- if spec_part != None:
- check_type(spec_part, value)
+ if spec_part is not None:
+ if value is not None:
+ id, list_indices = isc.cc.data.split_identifier_list_indices(identifier)
+ if list_indices is not None \
+ and spec_part['item_type'] == 'list':
+ spec_part = spec_part['list_item_spec']
+ check_type(spec_part, value)
+ else:
+ raise isc.cc.data.DataNotFoundError(identifier)
+
+ # Since we do not support list diffs (yet?), we need to
+ # copy the currently set list of items to _local_changes
+ # if we want to modify an element in there
+ # (for any list indices specified in the full identifier)
+ id_parts = isc.cc.data.split_identifier(identifier)
+ cur_id_part = '/'
+ for id_part in id_parts:
+ id, list_indices = isc.cc.data.split_identifier_list_indices(id_part)
+ if list_indices is not None:
+ cur_list, status = self.get_value(cur_id_part + id)
+ if status != MultiConfigData.LOCAL:
+ isc.cc.data.set(self._local_changes,
+ cur_id_part + id,
+ cur_list)
+ cur_id_part = cur_id_part + id_part + "/"
isc.cc.data.set(self._local_changes, identifier, value)
def get_config_item_list(self, identifier = None, recurse = False):
Modified: branches/trac347/src/lib/python/isc/config/module_spec.py
==============================================================================
--- branches/trac347/src/lib/python/isc/config/module_spec.py (original)
+++ branches/trac347/src/lib/python/isc/config/module_spec.py Mon Dec 27 11:50:02 2010
@@ -328,7 +328,24 @@
return True
def _validate_spec_list(module_spec, full, data, errors):
+ # we do not return immediately, there may be more errors
+ # so we keep a boolean to keep track if we found errors
+ validated = True
+
+ # check if the known items are correct
for spec_item in module_spec:
if not _validate_spec(spec_item, full, data, errors):
- return False
- return True
+ validated = False
+
+ # 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
+ return validated
Modified: branches/trac347/src/lib/python/isc/config/tests/ccsession_test.py
==============================================================================
--- branches/trac347/src/lib/python/isc/config/tests/ccsession_test.py (original)
+++ branches/trac347/src/lib/python/isc/config/tests/ccsession_test.py Mon Dec 27 11:50:02 2010
@@ -290,7 +290,7 @@
mccs = self.create_session("spec2.spec", None, None, fake_session)
mccs.set_config_handler(self.my_config_handler_ok)
self.assertEqual(len(fake_session.message_queue), 0)
- cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'Spec2': { 'item1': 2 }})
+ cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'item1': 2 })
fake_session.group_sendmsg(cmd, 'Spec2')
self.assertEqual(len(fake_session.message_queue), 1)
mccs.check_command()
@@ -303,12 +303,12 @@
mccs = self.create_session("spec2.spec", None, None, fake_session)
mccs.set_config_handler(self.my_config_handler_err)
self.assertEqual(len(fake_session.message_queue), 0)
- cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'Spec2': { 'item1': 'aaa' }})
+ cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'item1': 'aaa' })
fake_session.group_sendmsg(cmd, 'Spec2')
self.assertEqual(len(fake_session.message_queue), 1)
mccs.check_command()
self.assertEqual(len(fake_session.message_queue), 1)
- self.assertEqual({'result': [1, 'just an error']},
+ self.assertEqual({'result': [1, 'aaa should be an integer']},
fake_session.get_message('Spec2', None))
def test_check_command5(self):
@@ -316,12 +316,12 @@
mccs = self.create_session("spec2.spec", None, None, fake_session)
mccs.set_config_handler(self.my_config_handler_exc)
self.assertEqual(len(fake_session.message_queue), 0)
- cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'Spec2': { 'item1': 'aaa' }})
+ cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'item1': 'aaa' })
fake_session.group_sendmsg(cmd, 'Spec2')
self.assertEqual(len(fake_session.message_queue), 1)
mccs.check_command()
self.assertEqual(len(fake_session.message_queue), 1)
- self.assertEqual({'result': [1, 'just an exception']},
+ self.assertEqual({'result': [1, 'aaa should be an integer']},
fake_session.get_message('Spec2', None))
def test_check_command6(self):
@@ -416,7 +416,7 @@
mccs = self.create_session("spec2.spec", None, None, fake_session)
mccs.set_config_handler(self.my_config_handler_ok)
self.assertEqual(len(fake_session.message_queue), 0)
- cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'Spec2': { 'item1': 2 }})
+ cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'item1': 2 })
self.assertEqual(len(fake_session.message_queue), 0)
env = { 'group':'Spec2', 'from':None }
mccs.check_command_without_recvmsg(cmd, env)
@@ -559,6 +559,14 @@
mccs.check_command()
self.assertEqual(len(fake_session.message_queue), 0)
+
+class fakeData:
+ def decode(self):
+ return "{}";
+
+class fakeAnswer:
+ def read(self):
+ return fakeData();
class fakeUIConn():
def __init__(self):
@@ -581,7 +589,7 @@
if name in self.post_answers:
return self.post_answers[name]
else:
- return None
+ return fakeAnswer()
class TestUIModuleCCSession(unittest.TestCase):
@@ -637,6 +645,8 @@
self.assertEqual({'Spec2': {'item5': ['foo']}}, uccs._local_changes)
uccs.add_value("Spec2/item5", "foo")
self.assertEqual({'Spec2': {'item5': ['foo']}}, uccs._local_changes)
+ uccs.remove_value("Spec2/item5[0]", None)
+ self.assertEqual({'Spec2': {'item5': []}}, uccs._local_changes)
def test_commit(self):
fake_conn = fakeUIConn()
Modified: branches/trac347/src/lib/python/isc/config/tests/config_data_test.py
==============================================================================
--- branches/trac347/src/lib/python/isc/config/tests/config_data_test.py (original)
+++ branches/trac347/src/lib/python/isc/config/tests/config_data_test.py Mon Dec 27 11:50:02 2010
@@ -107,6 +107,8 @@
self.assertRaises(isc.cc.data.DataTypeError, convert_type, spec_part, "a")
self.assertRaises(isc.cc.data.DataTypeError, convert_type, spec_part, [ 1, 2 ])
self.assertRaises(isc.cc.data.DataTypeError, convert_type, spec_part, { "a": 1 })
+ self.assertRaises(isc.cc.data.DataTypeError, convert_type, 1, "a")
+ self.assertRaises(isc.cc.data.DataTypeError, convert_type, { 'somedict': 'somevalue' }, "a")
spec_part = find_spec_part(config_spec, "value2")
self.assertEqual(1.1, convert_type(spec_part, '1.1'))
@@ -142,6 +144,18 @@
self.assertRaises(isc.cc.data.DataTypeError, convert_type, spec_part, 1.1)
self.assertRaises(isc.cc.data.DataTypeError, convert_type, spec_part, True)
self.assertRaises(isc.cc.data.DataTypeError, convert_type, spec_part, "a")
+ self.assertRaises(isc.cc.data.DataTypeError, convert_type, spec_part, [ "a", "b" ])
+ self.assertRaises(isc.cc.data.DataTypeError, convert_type, spec_part, [ "1", "b" ])
+ self.assertRaises(isc.cc.data.DataTypeError, convert_type, spec_part, { "a": 1 })
+
+ spec_part = find_spec_part(config_spec, "value6")
+ self.assertEqual({}, convert_type(spec_part, '{}'))
+ self.assertEqual({ 'v61': 'a' }, convert_type(spec_part, '{ \'v61\': \'a\' }'))
+
+ self.assertRaises(isc.cc.data.DataTypeError, convert_type, spec_part, 1.1)
+ self.assertRaises(isc.cc.data.DataTypeError, convert_type, spec_part, True)
+ self.assertRaises(isc.cc.data.DataTypeError, convert_type, spec_part, "a")
+ self.assertRaises(isc.cc.data.DataTypeError, convert_type, spec_part, "1")
self.assertRaises(isc.cc.data.DataTypeError, convert_type, spec_part, [ "a", "b" ])
self.assertRaises(isc.cc.data.DataTypeError, convert_type, spec_part, [ "1", "b" ])
self.assertRaises(isc.cc.data.DataTypeError, convert_type, spec_part, { "a": 1 })
@@ -321,6 +335,8 @@
pass
def test_get_local_value(self):
+ module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec2.spec")
+ self.mcd.set_specification(module_spec)
value = self.mcd.get_local_value("Spec2/item1")
self.assertEqual(None, value)
self.mcd.set_value("Spec2/item1", 2)
@@ -342,6 +358,12 @@
self.assertEqual(1, value)
value = self.mcd.get_default_value("/Spec2/item1")
self.assertEqual(1, value)
+ value = self.mcd.get_default_value("Spec2/item5[0]")
+ self.assertEqual('a', value)
+ value = self.mcd.get_default_value("Spec2/item5[5]")
+ self.assertEqual(None, value)
+ value = self.mcd.get_default_value("Spec2/item5[0][1]")
+ self.assertEqual(None, value)
value = self.mcd.get_default_value("Spec2/item6/value1")
self.assertEqual('default', value)
value = self.mcd.get_default_value("Spec2/item6/value2")
@@ -353,19 +375,33 @@
module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec2.spec")
self.mcd.set_specification(module_spec)
self.mcd.set_value("Spec2/item1", 2)
- value,status = self.mcd.get_value("Spec2/item1")
+
+ value, status = self.mcd.get_value("Spec2/item1")
self.assertEqual(2, value)
self.assertEqual(MultiConfigData.LOCAL, status)
- value,status = self.mcd.get_value("Spec2/item2")
+
+ value, status = self.mcd.get_value("Spec2/item2")
self.assertEqual(1.1, value)
self.assertEqual(MultiConfigData.DEFAULT, status)
+
self.mcd._current_config = { "Spec2": { "item3": False } }
- value,status = self.mcd.get_value("Spec2/item3")
+
+ value, status = self.mcd.get_value("Spec2/item3")
self.assertEqual(False, value)
self.assertEqual(MultiConfigData.CURRENT, status)
- value,status = self.mcd.get_value("Spec2/no_such_item")
+
+ value, status = self.mcd.get_value("Spec2/no_such_item")
self.assertEqual(None, value)
self.assertEqual(MultiConfigData.NONE, status)
+
+ value, status = self.mcd.get_value("Spec2/item5[0]")
+ self.assertEqual("a", value)
+ self.assertEqual(MultiConfigData.DEFAULT, status)
+
+ value, status = self.mcd.get_value("Spec2/item5[0]", False)
+ self.assertEqual(None, value)
+ self.assertEqual(MultiConfigData.NONE, status)
+
def test_get_value_maps(self):
maps = self.mcd.get_value_maps()
@@ -390,29 +426,31 @@
self.mcd.set_value("Spec2/item3", False)
maps = self.mcd.get_value_maps("/Spec2")
self.assertEqual([{'default': False, 'type': 'integer', 'name': 'item1', 'value': 2, 'modified': False},
- {'default': False, 'type': 'real', 'name': 'item2', 'value': 1.1, 'modified': False},
+ {'default': True, 'type': 'real', 'name': 'item2', 'value': 1.1, 'modified': False},
{'default': False, 'type': 'boolean', 'name': 'item3', 'value': False, 'modified': True},
- {'default': False, 'type': 'string', 'name': 'item4', 'value': 'test', 'modified': False},
- {'default': False, 'type': 'list', 'name': 'item5', 'value': ['a', 'b'], 'modified': False},
- {'default': False, 'type': 'map', 'name': 'item6', 'value': {}, 'modified': False}], maps)
+ {'default': True, 'type': 'string', 'name': 'item4', 'value': 'test', 'modified': False},
+ {'default': True, 'type': 'list', 'name': 'item5', 'value': ['a', 'b'], 'modified': False},
+ {'default': True, 'type': 'map', 'name': 'item6', 'value': {}, 'modified': False}], maps)
maps = self.mcd.get_value_maps("Spec2")
self.assertEqual([{'default': False, 'type': 'integer', 'name': 'item1', 'value': 2, 'modified': False},
- {'default': False, 'type': 'real', 'name': 'item2', 'value': 1.1, 'modified': False},
+ {'default': True, 'type': 'real', 'name': 'item2', 'value': 1.1, 'modified': False},
{'default': False, 'type': 'boolean', 'name': 'item3', 'value': False, 'modified': True},
- {'default': False, 'type': 'string', 'name': 'item4', 'value': 'test', 'modified': False},
- {'default': False, 'type': 'list', 'name': 'item5', 'value': ['a', 'b'], 'modified': False},
- {'default': False, 'type': 'map', 'name': 'item6', 'value': {}, 'modified': False}], maps)
+ {'default': True, 'type': 'string', 'name': 'item4', 'value': 'test', 'modified': False},
+ {'default': True, 'type': 'list', 'name': 'item5', 'value': ['a', 'b'], 'modified': False},
+ {'default': True, 'type': 'map', 'name': 'item6', 'value': {}, 'modified': False}], maps)
maps = self.mcd.get_value_maps("/Spec2/item5")
self.assertEqual([{'default': False, 'type': 'string', 'name': 'list_element', 'value': 'a', 'modified': False},
{'default': False, 'type': 'string', 'name': 'list_element', 'value': 'b', 'modified': False}], maps)
+ maps = self.mcd.get_value_maps("/Spec2/item5[0]")
+ self.assertEqual([{'default': True, 'modified': False, 'name': 'list_element', 'type': 'string', 'value': 'a'}], maps)
maps = self.mcd.get_value_maps("/Spec2/item1")
self.assertEqual([{'default': False, 'type': 'integer', 'name': 'item1', 'value': 2, 'modified': False}], maps)
maps = self.mcd.get_value_maps("/Spec2/item2")
- self.assertEqual([{'default': False, 'type': 'real', 'name': 'item2', 'value': 1.1, 'modified': False}], maps)
+ self.assertEqual([{'default': True, 'type': 'real', 'name': 'item2', 'value': 1.1, 'modified': False}], maps)
maps = self.mcd.get_value_maps("/Spec2/item3")
self.assertEqual([{'default': False, 'type': 'boolean', 'name': 'item3', 'value': False, 'modified': True}], maps)
maps = self.mcd.get_value_maps("/Spec2/item4")
- self.assertEqual([{'default': False, 'type': 'string', 'name': 'item4', 'value': 'test', 'modified': False}], maps)
+ self.assertEqual([{'default': True, 'type': 'string', 'name': 'item4', 'value': 'test', 'modified': False}], maps)
module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec24.spec")
self.mcd.set_specification(module_spec)
@@ -428,8 +466,19 @@
module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec2.spec")
self.mcd.set_specification(module_spec)
self.mcd.set_value("Spec2/item1", 2)
- self.assertRaises(isc.cc.data.DataTypeError, self.mcd.set_value, "Spec2/item1", "asdf")
- self.mcd.set_value("Spec2/no_such_item", 4)
+ self.assertRaises(isc.cc.data.DataTypeError,
+ self.mcd.set_value, "Spec2/item1", "asdf")
+
+ self.assertRaises(isc.cc.data.DataNotFoundError,
+ self.mcd.set_value, "Spec2/no_such_item", 4)
+
+ self.mcd.set_value("Spec2/item5[0]", "c")
+ value, status = self.mcd.get_value("Spec2/item5[0]")
+ self.assertEqual(value, "c")
+ self.assertEqual(MultiConfigData.LOCAL, status)
+
+ self.assertRaises(isc.cc.data.DataTypeError, self.mcd.set_value, "Spec2/item5[a]", "asdf")
+
def test_get_config_item_list(self):
config_items = self.mcd.get_config_item_list()
@@ -446,6 +495,8 @@
self.assertEqual(['Spec2/item1', 'Spec2/item2', 'Spec2/item3', 'Spec2/item4', 'Spec2/item5', 'Spec2/item6/value1', 'Spec2/item6/value2'], config_items)
config_items = self.mcd.get_config_item_list("Spec2")
self.assertEqual(['Spec2/item1', 'Spec2/item2', 'Spec2/item3', 'Spec2/item4', 'Spec2/item5', 'Spec2/item6'], config_items)
+ config_items = self.mcd.get_config_item_list("/Spec2")
+ self.assertEqual(['Spec2/item1', 'Spec2/item2', 'Spec2/item3', 'Spec2/item4', 'Spec2/item5', 'Spec2/item6'], config_items)
config_items = self.mcd.get_config_item_list("Spec2", True)
self.assertEqual(['Spec2/item1', 'Spec2/item2', 'Spec2/item3', 'Spec2/item4', 'Spec2/item5', 'Spec2/item6/value1', 'Spec2/item6/value2'], config_items)
Modified: branches/trac347/src/lib/python/isc/config/tests/module_spec_test.py
==============================================================================
--- branches/trac347/src/lib/python/isc/config/tests/module_spec_test.py (original)
+++ branches/trac347/src/lib/python/isc/config/tests/module_spec_test.py Mon Dec 27 11:50:02 2010
@@ -312,6 +312,18 @@
self.assertEqual(False, isc.config.module_spec._validate_spec(spec, True, {}, None))
self.assertEqual(False, isc.config.module_spec._validate_spec(spec, True, {}, errors))
self.assertEqual(['non-optional item an_item missing'], errors)
+
+ def test_validate_unknown_items(self):
+ spec = [{ 'item_name': "an_item",
+ 'item_type': "string",
+ 'item_optional': True,
+ 'item_default': "asdf"
+ }]
+
+ errors = []
+ 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)
More information about the bind10-changes
mailing list