[svn] commit: r4022 - in /branches/trac362: ./ doc/ doc/guide/ src/bin/ 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/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/rdata/any_255/ 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 18:21:26 UTC 2010


Author: jreed
Date: Mon Dec 27 18:21:26 2010
New Revision: 4022

Log:
catch up to trunk, 2 conflicts.

Added:
    branches/trac362/src/bin/auth/config.cc
      - copied unchanged from r4021, trunk/src/bin/auth/config.cc
    branches/trac362/src/bin/auth/config.h
      - copied unchanged from r4021, trunk/src/bin/auth/config.h
    branches/trac362/src/bin/auth/query.cc
      - copied unchanged from r4021, trunk/src/bin/auth/query.cc
    branches/trac362/src/bin/auth/query.h
      - copied unchanged from r4021, trunk/src/bin/auth/query.h
    branches/trac362/src/bin/auth/tests/config_unittest.cc
      - copied unchanged from r4021, trunk/src/bin/auth/tests/config_unittest.cc
    branches/trac362/src/bin/auth/tests/query_unittest.cc
      - copied unchanged from r4021, trunk/src/bin/auth/tests/query_unittest.cc
    branches/trac362/src/bin/recurse/
      - copied from r4021, trunk/src/bin/recurse/
    branches/trac362/src/lib/asiolink/
      - copied from r4021, trunk/src/lib/asiolink/
    branches/trac362/src/lib/config/tests/testdata/data22_9.data
      - copied unchanged from r4021, trunk/src/lib/config/tests/testdata/data22_9.data
    branches/trac362/src/lib/datasrc/memory_datasrc.cc
      - copied unchanged from r4021, trunk/src/lib/datasrc/memory_datasrc.cc
    branches/trac362/src/lib/datasrc/memory_datasrc.h
      - copied unchanged from r4021, trunk/src/lib/datasrc/memory_datasrc.h
    branches/trac362/src/lib/datasrc/rbtree.h
      - copied unchanged from r4021, trunk/src/lib/datasrc/rbtree.h
    branches/trac362/src/lib/datasrc/result.h
      - copied unchanged from r4021, trunk/src/lib/datasrc/result.h
    branches/trac362/src/lib/datasrc/tests/memory_datasrc_unittest.cc
      - copied unchanged from r4021, trunk/src/lib/datasrc/tests/memory_datasrc_unittest.cc
    branches/trac362/src/lib/datasrc/tests/rbtree_unittest.cc
      - copied unchanged from r4021, trunk/src/lib/datasrc/tests/rbtree_unittest.cc
    branches/trac362/src/lib/datasrc/zone.h
      - copied unchanged from r4021, trunk/src/lib/datasrc/zone.h
    branches/trac362/src/lib/dns/masterload.cc
      - copied unchanged from r4021, trunk/src/lib/dns/masterload.cc
    branches/trac362/src/lib/dns/masterload.h
      - copied unchanged from r4021, trunk/src/lib/dns/masterload.h
    branches/trac362/src/lib/dns/rdata/any_255/
      - copied from r4021, trunk/src/lib/dns/rdata/any_255/
    branches/trac362/src/lib/dns/tests/masterload_unittest.cc
      - copied unchanged from r4021, trunk/src/lib/dns/tests/masterload_unittest.cc
    branches/trac362/src/lib/dns/tests/rdata_tsig_unittest.cc
      - copied unchanged from r4021, trunk/src/lib/dns/tests/rdata_tsig_unittest.cc
    branches/trac362/src/lib/dns/tests/testdata/masterload.txt
      - copied unchanged from r4021, trunk/src/lib/dns/tests/testdata/masterload.txt
    branches/trac362/src/lib/dns/tests/testdata/rdata_tsig_fromWire1.spec
      - copied unchanged from r4021, trunk/src/lib/dns/tests/testdata/rdata_tsig_fromWire1.spec
    branches/trac362/src/lib/dns/tests/testdata/rdata_tsig_fromWire2.spec
      - copied unchanged from r4021, trunk/src/lib/dns/tests/testdata/rdata_tsig_fromWire2.spec
    branches/trac362/src/lib/dns/tests/testdata/rdata_tsig_fromWire3.spec
      - copied unchanged from r4021, trunk/src/lib/dns/tests/testdata/rdata_tsig_fromWire3.spec
    branches/trac362/src/lib/dns/tests/testdata/rdata_tsig_fromWire4.spec
      - copied unchanged from r4021, trunk/src/lib/dns/tests/testdata/rdata_tsig_fromWire4.spec
    branches/trac362/src/lib/dns/tests/testdata/rdata_tsig_fromWire5.spec
      - copied unchanged from r4021, trunk/src/lib/dns/tests/testdata/rdata_tsig_fromWire5.spec
    branches/trac362/src/lib/dns/tests/testdata/rdata_tsig_fromWire6.spec
      - copied unchanged from r4021, trunk/src/lib/dns/tests/testdata/rdata_tsig_fromWire6.spec
    branches/trac362/src/lib/dns/tests/testdata/rdata_tsig_fromWire7.spec
      - copied unchanged from r4021, trunk/src/lib/dns/tests/testdata/rdata_tsig_fromWire7.spec
    branches/trac362/src/lib/dns/tests/testdata/rdata_tsig_fromWire8.spec
      - copied unchanged from r4021, trunk/src/lib/dns/tests/testdata/rdata_tsig_fromWire8.spec
    branches/trac362/src/lib/dns/tests/testdata/rdata_tsig_fromWire9.spec
      - copied unchanged from r4021, trunk/src/lib/dns/tests/testdata/rdata_tsig_fromWire9.spec
    branches/trac362/src/lib/dns/tests/testdata/rdata_tsig_toWire1.spec
      - copied unchanged from r4021, trunk/src/lib/dns/tests/testdata/rdata_tsig_toWire1.spec
    branches/trac362/src/lib/dns/tests/testdata/rdata_tsig_toWire2.spec
      - copied unchanged from r4021, trunk/src/lib/dns/tests/testdata/rdata_tsig_toWire2.spec
    branches/trac362/src/lib/dns/tests/testdata/rdata_tsig_toWire3.spec
      - copied unchanged from r4021, trunk/src/lib/dns/tests/testdata/rdata_tsig_toWire3.spec
    branches/trac362/src/lib/dns/tests/testdata/rdata_tsig_toWire4.spec
      - copied unchanged from r4021, trunk/src/lib/dns/tests/testdata/rdata_tsig_toWire4.spec
    branches/trac362/src/lib/dns/tests/testdata/rdata_tsig_toWire5.spec
      - copied unchanged from r4021, trunk/src/lib/dns/tests/testdata/rdata_tsig_toWire5.spec
    branches/trac362/src/lib/log/
      - copied from r4021, trunk/src/lib/log/
    branches/trac362/src/lib/nsas/
      - copied from r4021, trunk/src/lib/nsas/
    branches/trac362/src/lib/testutils/
      - copied from r4021, trunk/src/lib/testutils/
Removed:
    branches/trac362/src/bin/auth/asio_link.cc
    branches/trac362/src/bin/auth/asio_link.h
    branches/trac362/src/bin/auth/tests/asio_link_unittest.cc
Modified:
    branches/trac362/   (props changed)
    branches/trac362/ChangeLog
    branches/trac362/Makefile.am
    branches/trac362/README
    branches/trac362/configure.ac
    branches/trac362/doc/Doxyfile
    branches/trac362/doc/guide/bind10-guide.html
    branches/trac362/doc/guide/bind10-guide.xml
    branches/trac362/src/bin/Makefile.am
    branches/trac362/src/bin/auth/Makefile.am
    branches/trac362/src/bin/auth/auth.spec.pre.in
    branches/trac362/src/bin/auth/auth_srv.cc
    branches/trac362/src/bin/auth/auth_srv.h
    branches/trac362/src/bin/auth/benchmarks/Makefile.am
    branches/trac362/src/bin/auth/benchmarks/query_bench.cc
    branches/trac362/src/bin/auth/change_user.cc
    branches/trac362/src/bin/auth/common.h
    branches/trac362/src/bin/auth/main.cc
    branches/trac362/src/bin/auth/tests/Makefile.am
    branches/trac362/src/bin/auth/tests/auth_srv_unittest.cc
    branches/trac362/src/bin/auth/tests/testdata/   (props changed)
    branches/trac362/src/bin/bind10/bind10.py.in   (contents, props changed)
    branches/trac362/src/bin/bind10/bob.spec
    branches/trac362/src/bin/bind10/run_bind10.sh.in
    branches/trac362/src/bin/bind10/tests/bind10_test.py
    branches/trac362/src/bin/bindctl/bindcmd.py
    branches/trac362/src/bin/bindctl/bindctl-source.py.in
    branches/trac362/src/bin/bindctl/run_bindctl.sh.in
    branches/trac362/src/bin/cmdctl/run_b10-cmdctl.sh.in
    branches/trac362/src/bin/loadzone/run_loadzone.sh.in
    branches/trac362/src/bin/msgq/msgq.py.in
    branches/trac362/src/bin/msgq/run_msgq.sh.in
    branches/trac362/src/bin/stats/run_b10-stats.sh.in
    branches/trac362/src/bin/stats/run_b10-stats_stub.sh.in
    branches/trac362/src/bin/usermgr/run_b10-cmdctl-usermgr.sh.in
    branches/trac362/src/bin/xfrin/b10-xfrin.xml
    branches/trac362/src/bin/xfrin/run_b10-xfrin.sh.in
    branches/trac362/src/bin/xfrout/b10-xfrout.8
    branches/trac362/src/bin/xfrout/b10-xfrout.xml
    branches/trac362/src/bin/xfrout/run_b10-xfrout.sh.in
    branches/trac362/src/bin/xfrout/xfrout.spec.pre.in
    branches/trac362/src/bin/zonemgr/run_b10-zonemgr.sh.in
    branches/trac362/src/lib/Makefile.am
    branches/trac362/src/lib/cc/session.cc
    branches/trac362/src/lib/config/config_data.cc
    branches/trac362/src/lib/config/module_spec.cc
    branches/trac362/src/lib/config/tests/config_data_unittests.cc
    branches/trac362/src/lib/config/tests/module_spec_unittests.cc
    branches/trac362/src/lib/config/tests/testdata/Makefile.am
    branches/trac362/src/lib/config/tests/testdata/data22_8.data
    branches/trac362/src/lib/datasrc/Makefile.am
    branches/trac362/src/lib/datasrc/data_source.h
    branches/trac362/src/lib/datasrc/static_datasrc.cc
    branches/trac362/src/lib/datasrc/tests/Makefile.am
    branches/trac362/src/lib/datasrc/tests/datasrc_unittest.cc
    branches/trac362/src/lib/datasrc/tests/static_unittest.cc
    branches/trac362/src/lib/datasrc/tests/zonetable_unittest.cc
    branches/trac362/src/lib/datasrc/zonetable.cc
    branches/trac362/src/lib/datasrc/zonetable.h
    branches/trac362/src/lib/dns/Makefile.am
    branches/trac362/src/lib/dns/buffer.h
    branches/trac362/src/lib/dns/message.h
    branches/trac362/src/lib/dns/messagerenderer.h
    branches/trac362/src/lib/dns/rrclass-placeholder.h
    branches/trac362/src/lib/dns/tests/Makefile.am
    branches/trac362/src/lib/dns/tests/testdata/Makefile.am
    branches/trac362/src/lib/dns/tests/testdata/edns_toWire4.spec
    branches/trac362/src/lib/dns/tests/testdata/gen-wiredata.py.in
    branches/trac362/src/lib/dns/tests/tsigkey_unittest.cc
    branches/trac362/src/lib/dns/tests/unittest_util.cc
    branches/trac362/src/lib/dns/tests/unittest_util.h
    branches/trac362/src/lib/python/isc/cc/data.py
    branches/trac362/src/lib/python/isc/cc/tests/data_test.py
    branches/trac362/src/lib/python/isc/config/ccsession.py
    branches/trac362/src/lib/python/isc/config/config_data.py
    branches/trac362/src/lib/python/isc/config/module_spec.py
    branches/trac362/src/lib/python/isc/config/tests/ccsession_test.py
    branches/trac362/src/lib/python/isc/config/tests/config_data_test.py
    branches/trac362/src/lib/python/isc/config/tests/module_spec_test.py
    branches/trac362/src/lib/python/isc/utils/   (props changed)

Modified: branches/trac362/ChangeLog
==============================================================================
--- branches/trac362/ChangeLog (original)
+++ branches/trac362/ChangeLog Mon Dec 27 18:21:26 2010
@@ -1,6 +1,121 @@
   XXX.  [build]		jreed
 	Introduced configure option and make targets for generating
 	Python code coverage report. (Trac #362)
+
+  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
+	both). A dummy recursor has been provided for test purposes.
+	(Trac #412, svn r3676)
+
+  121.  [func]		jinmei
+	src/lib/dns: Added support for TSIG RDATA.  At this moment this is
+	not much of real use, however, because no protocol support was
+	added yet.  It will soon be added. (Trac #372, svn r3649)
 
   120.  [func]		jinmei
 	src/lib/dns: introduced two new classes, TSIGKey and TSIGKeyRing,
@@ -14,13 +129,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
@@ -56,15 +172,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
@@ -76,7 +190,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])
 
@@ -103,11 +217,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)
@@ -160,7 +274,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
@@ -340,7 +454,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
@@ -462,7 +576,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
@@ -470,7 +584,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
@@ -478,7 +592,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.
@@ -491,7 +605,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
@@ -536,7 +650,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/trac362/Makefile.am
==============================================================================
--- branches/trac362/Makefile.am (original)
+++ branches/trac362/Makefile.am Mon Dec 27 18:21:26 2010
@@ -47,7 +47,7 @@
 			\*_unittest.cc \
 			\*_unittests.h \
 			--output report.info ; \
-		$(GENHTML) -o $(abs_top_builddir)/coverage-cpp-html report.info ; \
+		$(GENHTML) --legend -o $(abs_top_builddir)/coverage-cpp-html report.info ; \
 		echo "Generated C++ Code Coverage report in HTML at $(abs_top_builddir)/coverage-cpp-html" ; \
 	else \
 		echo "C++ code coverage not enabled at configuration time." ; \

Modified: branches/trac362/README
==============================================================================
--- branches/trac362/README (original)
+++ branches/trac362/README Mon Dec 27 18:21:26 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/trac362/configure.ac
==============================================================================
--- branches/trac362/configure.ac (original)
+++ branches/trac362/configure.ac Mon Dec 27 18:21:26 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
@@ -338,10 +371,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],,
   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.
@@ -407,6 +496,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.
@@ -485,8 +576,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
@@ -503,6 +595,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
@@ -537,6 +631,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
@@ -551,6 +650,8 @@
            src/bin/xfrout/xfrout.spec.pre
            src/bin/xfrout/tests/xfrout_test
            src/bin/xfrout/run_b10-xfrout.sh
+           src/bin/recurse/recurse.spec.pre
+           src/bin/recurse/spec_config.h.pre
            src/bin/zonemgr/zonemgr.py
            src/bin/zonemgr/zonemgr.spec.pre
            src/bin/zonemgr/tests/zonemgr_test
@@ -593,6 +694,7 @@
            chmod +x src/bin/cmdctl/run_b10-cmdctl.sh
            chmod +x src/bin/xfrin/run_b10-xfrin.sh
            chmod +x src/bin/xfrout/run_b10-xfrout.sh
+           chmod +x src/bin/recurse/run_b10-recurse.sh
            chmod +x src/bin/zonemgr/run_b10-zonemgr.sh
            chmod +x src/bin/stats/tests/stats_test
            chmod +x src/bin/stats/run_b10-stats.sh

Modified: branches/trac362/doc/Doxyfile
==============================================================================
--- branches/trac362/doc/Doxyfile (original)
+++ branches/trac362/doc/Doxyfile Mon Dec 27 18:21:26 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/trac362/doc/guide/bind10-guide.html
==============================================================================
--- branches/trac362/doc/guide/bind10-guide.html (original)
+++ branches/trac362/doc/guide/bind10-guide.html Mon Dec 27 18:21:26 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/trac362/doc/guide/bind10-guide.xml
==============================================================================
--- branches/trac362/doc/guide/bind10-guide.xml (original)
+++ branches/trac362/doc/guide/bind10-guide.xml Mon Dec 27 18:21:26 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/trac362/src/bin/Makefile.am
==============================================================================
--- branches/trac362/src/bin/Makefile.am (original)
+++ branches/trac362/src/bin/Makefile.am Mon Dec 27 18:21:26 2010
@@ -1,4 +1,4 @@
 SUBDIRS = bind10 bindctl cfgmgr loadzone msgq host cmdctl auth xfrin xfrout \
-	usermgr zonemgr stats tests
+	usermgr zonemgr stats tests recurse
 
 check-recursive: all-recursive

Modified: branches/trac362/src/bin/auth/Makefile.am
==============================================================================
--- branches/trac362/src/bin/auth/Makefile.am (original)
+++ branches/trac362/src/bin/auth/Makefile.am Mon Dec 27 18:21:26 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 += main.cc
 b10_auth_LDADD =  $(top_builddir)/src/lib/datasrc/libdatasrc.la
@@ -64,7 +47,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/trac362/src/bin/auth/auth.spec.pre.in
==============================================================================
--- branches/trac362/src/bin/auth/auth.spec.pre.in (original)
+++ branches/trac362/src/bin/auth/auth.spec.pre.in Mon Dec 27 18:21:26 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/trac362/src/bin/auth/auth_srv.cc
==============================================================================
--- branches/trac362/src/bin/auth/auth_srv.cc (original)
+++ branches/trac362/src/bin/auth/auth_srv.cc Mon Dec 27 18:21:26 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>
 
 using namespace std;
 
@@ -57,11 +62,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:
@@ -73,38 +79,45 @@
     ~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_;
+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_;
 };
 
 AuthSrvImpl::AuthSrvImpl(const bool use_cache,
                          AbstractXfroutClient& xfrout_client) :
     config_session_(NULL), verbose_mode_(false),
     xfrin_session_(NULL),
+    memory_datasrc_class_(RRClass::IN()),
     xfrout_connected_(false),
     xfrout_client_(xfrout_client)
 {
@@ -126,60 +139,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;
     }
 }
 }
@@ -219,143 +299,188 @@
     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());
 
     if (remote_edns) {
         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)
 {
     if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
         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);
     }
 
@@ -382,8 +507,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);
     }
 
@@ -391,28 +515,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);
     }
 
@@ -470,10 +592,12 @@
         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);
 }
 
@@ -535,6 +659,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/trac362/src/bin/auth/auth_srv.h
==============================================================================
--- branches/trac362/src/bin/auth/auth_srv.h (original)
+++ branches/trac362/src/bin/auth/auth_srv.h Mon Dec 27 18:21:26 2010
@@ -19,24 +19,24 @@
 
 #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>
+
 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.
@@ -84,11 +84,26 @@
             isc::xfr::AbstractXfroutClient& xfrout_client);
     ~AuthSrv();
     //@}
-    /// \return \c true if the \a message contains a response to be returned;
-    /// otherwise \c false.
-    bool processMessage(const asio_link::IOMessage& io_message,
-                        isc::dns::Message& message,
-                        isc::dns::MessageRenderer& response_renderer);
+
+    /// \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
+    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.
     ///
@@ -103,6 +118,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.
@@ -114,9 +131,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,
@@ -161,6 +180,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.
     ///
@@ -199,8 +233,60 @@
     /// is shutdown.
     ///
     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);
+
 private:
     AuthSrvImpl* impl_;
+    asiolink::IOService* io_service_;
+    asiolink::SimpleCallback* checkin_;
+    asiolink::DNSLookup* dns_lookup_;
+    asiolink::DNSAnswer* dns_answer_;
 };
 
 #endif // __AUTH_SRV_H

Modified: branches/trac362/src/bin/auth/benchmarks/Makefile.am
==============================================================================
--- branches/trac362/src/bin/auth/benchmarks/Makefile.am (original)
+++ branches/trac362/src/bin/auth/benchmarks/Makefile.am Mon Dec 27 18:21:26 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_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
 query_bench_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
@@ -17,5 +19,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/trac362/src/bin/auth/benchmarks/query_bench.cc
==============================================================================
--- branches/trac362/src/bin/auth/benchmarks/query_bench.cc (original)
+++ branches/trac362/src/bin/auth/benchmarks/query_bench.cc Mon Dec 27 18:21:26 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/trac362/src/bin/auth/change_user.cc
==============================================================================
--- branches/trac362/src/bin/auth/change_user.cc (original)
+++ branches/trac362/src/bin/auth/change_user.cc Mon Dec 27 18:21:26 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/trac362/src/bin/auth/common.h
==============================================================================
--- branches/trac362/src/bin/auth/common.h (original)
+++ branches/trac362/src/bin/auth/common.h Mon Dec 27 18:21:26 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/trac362/src/bin/auth/main.cc
==============================================================================
--- branches/trac362/src/bin/auth/main.cc (original)
+++ branches/trac362/src/bin/auth/main.cc Mon Dec 27 18:21:26 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,20 +52,22 @@
 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";
 
 /* 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) {
@@ -80,15 +83,24 @@
         /* let's add that message to our answer as well */
         answer = createAnswer(0, args);
     } else if (command == "shutdown") {
-        io_service->stop();
-    }
-    
+        io_service.stop();
+    }
+
     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);
 }
 } // end of anonymous namespace
@@ -140,12 +152,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();
     }
 
@@ -176,6 +190,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
@@ -183,15 +202,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,
@@ -203,25 +224,33 @@
             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;
 
-        // 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);
+
+        // 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());
 
         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;
     }
 
@@ -232,7 +261,6 @@
     delete xfrin_session;
     delete config_session;
     delete cc_session;
-    delete io_service;
     delete auth_server;
 
     return (ret);

Modified: branches/trac362/src/bin/auth/tests/Makefile.am
==============================================================================
--- branches/trac362/src/bin/auth/tests/Makefile.am (original)
+++ branches/trac362/src/bin/auth/tests/Makefile.am Mon Dec 27 18:21:26 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,10 +19,13 @@
 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 += 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 += run_unittests.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
@@ -32,10 +33,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/trac362/src/bin/auth/tests/auth_srv_unittest.cc
==============================================================================
--- branches/trac362/src/bin/auth/tests/auth_srv_unittest.cc (original)
+++ branches/trac362/src/bin/auth/tests/auth_srv_unittest.cc Mon Dec 27 18:21:26 2010
@@ -15,35 +15,16 @@
 // $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 <dns/tests/unittest_util.h>
-
-using isc::UnitTestUtil;
-using namespace std;
+#include <testutils/srv_unittest.h>
+
 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 =
@@ -52,445 +33,84 @@
 // 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(&notify_session);
     }
-    ~AuthSrvTest() {
-        delete io_message;
-        delete endpoint;
-    }
-    MockSession notify_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());
 }
@@ -498,19 +118,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.
@@ -522,10 +144,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
@@ -534,31 +157,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());
@@ -566,17 +189,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());
 }
 
@@ -587,126 +210,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"));
 
@@ -723,9 +347,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);
 }
 
@@ -737,9 +361,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);
 }
 
@@ -752,10 +376,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) {

Modified: branches/trac362/src/bin/bind10/bind10.py.in
==============================================================================
--- branches/trac362/src/bin/bind10/bind10.py.in (original)
+++ branches/trac362/src/bin/bind10/bind10.py.in Mon Dec 27 18:21:26 2010
@@ -15,7 +15,7 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-"""\
+"""
 This file implements the Boss of Bind (BoB, or bob) program.
 
 Its purpose is to start up the BIND 10 system, and then manage the
@@ -72,7 +72,7 @@
 # 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 = "bind10 20100916 (BIND 10 @PACKAGE_VERSION@)"
+VERSION = "bind10 20101129 (BIND 10 @PACKAGE_VERSION@)"
 
 # This is for bind10.boottime of stats module
 _BASETIME = time.gmtime()
@@ -189,72 +189,347 @@
     def respawn(self):
         self._spawn()
 
+class CChannelConnectError(Exception): pass
+
 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):
-        """Initialize the Boss of BIND. This is a singleton (only one
-        can run).
+    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).
         
-        The msgq_socket_file specifies the UNIX domain socket file
-        that the msgq process listens on.
-        If verbose is True, then the boss reports what it is doing.
-        """
-        self.verbose = verbose
-        self.msgq_socket_file = msgq_socket_file
-        self.auth_port = auth_port
-        self.address = None
-        if address:
-            self.address = address
+            The msgq_socket_file specifies the UNIX domain socket file that the
+            msgq process listens on.  If verbose is True, then the boss reports
+            what it is doing.
+        """
+        self.address = address
+        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
+        self.cfg_start_recurse = False
+        self.curproc = None
+        self.dead_processes = {}
+        self.msgq_socket_file = msgq_socket_file
+        self.nocache = nocache
         self.processes = {}
-        self.dead_processes = {}
         self.runnable = False
         self.uid = setuid
         self.username = username
-        self.nocache = nocache
+        self.verbose = verbose
 
     def config_handler(self, new_config):
         if self.verbose:
-            sys.stdout.write("[bind10] handling new config:\n")
-            sys.stdout.write(new_config + "\n")
+            sys.stdout.write("[bind10] Handling new configuration: " +
+                str(new_config) + "\n")
         answer = isc.config.ccsession.create_answer(0)
         return answer
         # TODO
 
     def command_handler(self, command, args):
         if self.verbose:
-            sys.stdout.write("[bind10] Boss got command:\n")
-            sys.stdout.write(command + "\n")
+            sys.stdout.write("[bind10] Boss got command: " + command + "\n")
         answer = isc.config.ccsession.create_answer(1, "command not implemented")
         if type(command) != str:
             answer = isc.config.ccsession.create_answer(1, "bad command")
         else:
-            cmd = command
-            if cmd == "shutdown":
-                sys.stdout.write("[bind10] got shutdown command\n")
+            if command == "shutdown":
                 self.runnable = False
                 answer = isc.config.ccsession.create_answer(0)
             else:
                 answer = isc.config.ccsession.create_answer(1, 
                                                             "Unknown command")
         return answer
+
+    def kill_started_processes(self):
+        """
+            Called as part of the exception handling when a process fails to
+            start, this runs through the list of started processes, killing
+            each one.  It then clears that list.
+        """
+        if self.verbose:
+            sys.stdout.write("[bind10] killing started processes:\n")
+
+        for pid in self.processes:
+            if self.verbose:
+                sys.stdout.write("[bind10] - %s\n" % self.processes[pid].name)
+            self.processes[pid].process.kill()
+        self.processes = {}
+
+    def read_bind10_config(self):
+        """
+            Reads the parameters associated with the BoB module itself.
+
+            At present these are the components to start although arguably this
+            information should be in the configuration for the appropriate
+            module itself. (However, this would cause difficulty in the case of
+            xfrin/xfrout and zone manager as we don't need to start those if we
+            are not running the authoritative server.)
+        """
+        if self.verbose:
+            sys.stdout.write("[bind10] Reading Boss configuration:\n")
+
+        config_data = self.ccs.get_full_config()
+        self.cfg_start_auth = config_data.get("start_auth")
+        self.cfg_start_recurse = config_data.get("start_recurse")
+
+        if self.verbose:
+            sys.stdout.write("[bind10] - start_auth: %s\n" %
+                str(self.cfg_start_auth))
+            sys.stdout.write("[bind10] - start_recurse: %s\n" %
+                str(self.cfg_start_recurse))
+
+    def log_starting(self, process, port = None, address = None):
+        """
+            A convenience function to output a "Starting xxx" message if the
+            verbose option is set.  Putting this into a separate method ensures
+            that the output form is consistent across all processes.
+
+            The process name (passed as the first argument) is put into
+            self.curproc, and is used to indicate which process failed to
+            start if there is an error (and is used in the "Started" message
+            on success).  The optional port and address information are
+            appended to the message (if present).
+        """
+        self.curproc = process
+        if self.verbose:
+            sys.stdout.write("[bind10] Starting %s" % self.curproc)
+            if port is not None:
+                sys.stdout.write(" on port %d" % port)
+                if address is not None:
+                    sys.stdout.write(" (address %s)" % str(address))
+            sys.stdout.write("\n")
+
+    def log_started(self, pid = None):
+        """
+            A convenience function to output a 'Started xxxx (PID yyyy)'
+            message.  As with starting_message(), this ensures a consistent
+            format.
+        """
+        if self.verbose:
+            sys.stdout.write("[bind10] Started %s" % self.curproc)
+            if pid is not None:
+                sys.stdout.write(" (PID %d)" % pid)
+            sys.stdout.write("\n")
+
+    # The next few methods start the individual processes of BIND-10.  They
+    # are called via start_all_process().  If any fail, an exception is raised
+    # which is caught by the caller of start_all_processes(); this kills
+    # processes started up to that point before terminating the program.
+
+    def start_msgq(self, c_channel_env):
+        """
+            Start the message queue and connect to the command channel.
+        """
+        self.log_starting("b10-msgq")
+        c_channel = ProcessInfo("b10-msgq", ["b10-msgq"], c_channel_env,
+                                True, not self.verbose, uid=self.uid,
+                                username=self.username)
+        self.processes[c_channel.pid] = c_channel
+        self.log_started(c_channel.pid)
+
+        # Now connect to the c-channel
+        cc_connect_start = time.time()
+        while self.cc_session is None:
+            # if we have been trying for "a while" give up
+            if (time.time() - cc_connect_start) > 5:
+                raise CChannelConnectError("Unable to connect to c-channel after 5 seconds")
+
+            # try to connect, and if we can't wait a short while
+            try:
+                self.cc_session = isc.cc.Session(self.msgq_socket_file)
+            except isc.cc.session.SessionError:
+                time.sleep(0.1)
+
+    def start_cfgmgr(self, c_channel_env):
+        """
+            Starts the configuration manager process
+        """
+        self.log_starting("b10-cfgmgr")
+        bind_cfgd = ProcessInfo("b10-cfgmgr", ["b10-cfgmgr"],
+                                c_channel_env, uid=self.uid,
+                                username=self.username)
+        self.processes[bind_cfgd.pid] = bind_cfgd
+        self.log_started(bind_cfgd.pid)
+
+        # sleep until b10-cfgmgr is fully up and running, this is a good place
+        # to have a (short) timeout on synchronized groupsend/receive
+        # TODO: replace the sleep by a listen for ConfigManager started
+        # message
+        time.sleep(1)
+
+    def start_ccsession(self, c_channel_env):
+        """
+            Start the CC Session
+
+            The argument c_channel_env is unused but is supplied to keep the
+            argument list the same for all start_xxx methods.
+        """
+        self.log_starting("ccsession")
+        self.ccs = isc.config.ModuleCCSession(SPECFILE_LOCATION, 
+                                      self.config_handler, self.command_handler)
+        self.ccs.start()
+        self.log_started()
+
+    # A couple of utility methods for starting processes...
+
+    def start_process(self, name, args, c_channel_env, port=None, address=None):
+        """
+            Given a set of command arguments, start the process and output
+            appropriate log messages.  If the start is successful, the process
+            is added to the list of started processes.
+
+            The port and address arguments are for log messages only.
+        """
+        self.log_starting(name, port, address)
+        newproc = ProcessInfo(name, args, c_channel_env)
+        self.processes[newproc.pid] = newproc
+        self.log_started(newproc.pid)
+
+    def start_simple(self, name, c_channel_env, port=None, address=None):
+        """
+            Most of the BIND-10 processes are started with the command:
+
+                <process-name> [-v]
+
+            ... where -v is appended if verbose is enabled.  This method
+            generates the arguments from the name and starts the process.
+
+            The port and address arguments are for log messages only.
+        """
+        # Set up the command arguments.
+        args = [name]
+        if self.verbose:
+            args += ['-v']
+
+        # ... and start the process
+        self.start_process(name, args, c_channel_env, port, address)
+
+    # The next few methods start up the rest of the BIND-10 processes.
+    # Although many of these methods are little more than a call to
+    # start_simple, they are retained (a) for testing reasons and (b) as a place
+    # where modifications can be made if the process start-up sequence changes
+    # for a given process.
+
+    def start_auth(self, c_channel_env):
+        """
+            Start the Authoritative server
+        """
+        # XXX: this must be read from the configuration manager in the future
+        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:
+            dnsargs += ['-u', str(self.uid)]
+        if self.verbose:
+            dnsargs += ['-v']
+
+        # ... and start
+        self.start_process("b10-auth", dnsargs, c_channel_env,
+            self.dns_port, self.address)
+
+    def start_recurse(self, c_channel_env):
+        """
+            Start the Resolver.  At present, all these arguments and switches
+            are pure speculation.  As with the auth daemon, they should be
+            read from the configuration database.
+        """
+        self.curproc = "b10-recurse"
+        # XXX: this must be read from the configuration manager in the future
+        resargs = ['b10-recurse']
+        if self.uid:
+            resargs += ['-u', str(self.uid)]
+        if self.verbose:
+            resargs += ['-v']
+
+        # ... and start
+        self.start_process("b10-recurse", resargs, c_channel_env)
+
+    def start_xfrout(self, c_channel_env):
+        self.start_simple("b10-xfrout", c_channel_env)
+
+    def start_xfrin(self, c_channel_env):
+        self.start_simple("b10-xfrin", c_channel_env)
+
+    def start_zonemgr(self, c_channel_env):
+        self.start_simple("b10-zonemgr", c_channel_env)
+
+    def start_stats(self, c_channel_env):
+        self.start_simple("b10-stats", c_channel_env)
+
+    def start_cmdctl(self, c_channel_env):
+        # XXX: we hardcode port 8080
+        self.start_simple("b10-cmdctl", c_channel_env, 8080)
+
+    def start_all_processes(self, c_channel_env):
+        """
+            Starts up all the processes.  Any exception generated during the
+            starting of the processes is handled by the caller.
+        """
+        self.start_msgq(c_channel_env)
+        self.start_cfgmgr(c_channel_env)
+        self.start_ccsession(c_channel_env)
+
+        # Extract the parameters associated with Bob.  This can only be
+        # done after the CC Session is started.
+        self.read_bind10_config()
+
+        # Continue starting the processes.  The authoritative server (if
+        # selected):
+        if self.cfg_start_auth:
+            self.start_auth(c_channel_env)
+
+        # ... and resolver (if selected):
+        if self.cfg_start_recurse:
+            self.start_recurse(c_channel_env)
+
+        # Everything after the main components can run as non-root.
+        # TODO: this is only temporary - once the privileged socket creator is
+        # fully working, nothing else will run as root.
+        if self.uid is not None:
+            posix.setuid(self.uid)
+
+        # xfrin/xfrout and the zone manager are only meaningful if the
+        # authoritative server has been started.
+        if self.cfg_start_auth:
+            self.start_xfrout(c_channel_env)
+            self.start_xfrin(c_channel_env)
+            self.start_zonemgr(c_channel_env)
+
+        # ... and finally start the remaining processes
+        self.start_stats(c_channel_env)
+        self.start_cmdctl(c_channel_env)
     
     def startup(self):
-        """Start the BoB instance.
+        """
+            Start the BoB instance.
  
-        Returns None if successful, otherwise an string describing the
-        problem.
-        """
-        # try to connect to the c-channel daemon, 
-        # to see if it is already running
+            Returns None if successful, otherwise an string describing the
+            problem.
+        """
+        # Try to connect to the c-channel daemon, to see if it is already
+        # running
         c_channel_env = {}
         if self.msgq_socket_file is not None:
              c_channel_env["BIND10_MSGQ_SOCKET_FILE"] = self.msgq_socket_file 
         if self.verbose:
-            sys.stdout.write("[bind10] Checking for already running b10-msgq\n")
+           sys.stdout.write("[bind10] Checking for already running b10-msgq\n")
         # try to connect, and if we can't wait a short while
         try:
             self.cc_session = isc.cc.Session(self.msgq_socket_file)
@@ -263,209 +538,30 @@
             # this is the case we want, where the msgq is not running
             pass
 
-        # start the c-channel daemon
-        if self.verbose:
-            if self.msgq_socket_file:
-                sys.stdout.write("[bind10] Starting b10-msgq\n")
+        # Start all processes.  If any one fails to start, kill all started
+        # processes and exit with an error indication.
         try:
-            c_channel = ProcessInfo("b10-msgq", ["b10-msgq"], c_channel_env,
-                                    True, not self.verbose, uid=self.uid,
-                                    username=self.username)
+            self.start_all_processes(c_channel_env)
         except Exception as e:
-            return "Unable to start b10-msgq; " + str(e)
-        self.processes[c_channel.pid] = c_channel
-        if self.verbose:
-            sys.stdout.write("[bind10] Started b10-msgq (PID %d)\n" % 
-                             c_channel.pid)
-
-        # now connect to the c-channel
-        cc_connect_start = time.time()
-        while self.cc_session is None:
-            # if we have been trying for "a while" give up
-            if (time.time() - cc_connect_start) > 5:
-                c_channel.process.kill()
-                return "Unable to connect to c-channel after 5 seconds"
-            # try to connect, and if we can't wait a short while
-            try:
-                self.cc_session = isc.cc.Session(self.msgq_socket_file)
-            except isc.cc.session.SessionError:
-                time.sleep(0.1)
-
-        # start the configuration manager
-        if self.verbose:
-            sys.stdout.write("[bind10] Starting b10-cfgmgr\n")
-        try:
-            bind_cfgd = ProcessInfo("b10-cfgmgr", ["b10-cfgmgr"],
-                                    c_channel_env, uid=self.uid,
-                                    username=self.username)
-        except Exception as e:
-            c_channel.process.kill()
-            return "Unable to start b10-cfgmgr; " + str(e)
-        self.processes[bind_cfgd.pid] = bind_cfgd
-        if self.verbose:
-            sys.stdout.write("[bind10] Started b10-cfgmgr (PID %d)\n" % 
-                             bind_cfgd.pid)
-
-        # sleep until b10-cfgmgr is fully up and running, this is a good place
-        # to have a (short) timeout on synchronized groupsend/receive
-        # TODO: replace the sleep by a listen for ConfigManager started
-        # message
-        time.sleep(1)
-        if self.verbose:
-            sys.stdout.write("[bind10] starting ccsession\n")
-        self.ccs = isc.config.ModuleCCSession(SPECFILE_LOCATION, 
-                                      self.config_handler, self.command_handler)
-        self.ccs.start()
-        if self.verbose:
-            sys.stdout.write("[bind10] ccsession started\n")
-
-        # start b10-auth
-        # 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.uid:
-            authargs += ['-u', str(self.uid)]
-        if self.verbose:
-            authargs += ['-v']
-            sys.stdout.write("Starting b10-auth using port %d" %
-                             self.auth_port)
-            if self.address:
-                sys.stdout.write(" on %s" % str(self.address))
-            sys.stdout.write("\n")
-        try:
-            auth = ProcessInfo("b10-auth", authargs,
-                               c_channel_env)
-        except Exception as e:
-            c_channel.process.kill()
-            bind_cfgd.process.kill()
-            xfrout.process.kill()
-            return "Unable to start b10-auth; " + str(e)
-        self.processes[auth.pid] = auth
-        if self.verbose:
-            sys.stdout.write("[bind10] Started b10-auth (PID %d)\n" % auth.pid)
-
-        # everything after the authoritative server can run as non-root
-        if self.uid is not None:
-            posix.setuid(self.uid)
-
-        # start the xfrout before auth-server, to make sure every xfr-query can
-        # be processed properly.
-        xfrout_args = ['b10-xfrout']
-        if self.verbose:
-            sys.stdout.write("[bind10] Starting b10-xfrout\n")
-            xfrout_args += ['-v']
-        try:
-            xfrout = ProcessInfo("b10-xfrout", xfrout_args, 
-                                 c_channel_env )
-        except Exception as e:
-            c_channel.process.kill()
-            bind_cfgd.process.kill()
-            return "Unable to start b10-xfrout; " + str(e)
-        self.processes[xfrout.pid] = xfrout
-        if self.verbose:
-            sys.stdout.write("[bind10] Started b10-xfrout (PID %d)\n" % 
-                             xfrout.pid)
-
-        # start b10-xfrin
-        xfrin_args = ['b10-xfrin']
-        if self.verbose:
-            sys.stdout.write("[bind10] Starting b10-xfrin\n")
-            xfrin_args += ['-v']
-        try:
-            xfrind = ProcessInfo("b10-xfrin", xfrin_args,
-                                 c_channel_env)
-        except Exception as e:
-            c_channel.process.kill()
-            bind_cfgd.process.kill()
-            xfrout.process.kill()
-            auth.process.kill()
-            return "Unable to start b10-xfrin; " + str(e)
-        self.processes[xfrind.pid] = xfrind
-        if self.verbose:
-            sys.stdout.write("[bind10] Started b10-xfrin (PID %d)\n" % 
-                             xfrind.pid)
-
-        # start b10-zonemgr
-        zonemgr_args = ['b10-zonemgr']
-        if self.verbose:
-            sys.stdout.write("[bind10] Starting b10-zonemgr\n")
-            zonemgr_args += ['-v']
-        try:
-            zonemgr = ProcessInfo("b10-zonemgr", zonemgr_args,
-                                 c_channel_env)
-        except Exception as e:
-            c_channel.process.kill()
-            bind_cfgd.process.kill()
-            xfrout.process.kill()
-            auth.process.kill()
-            xfrind.process.kill()
-            return "Unable to start b10-zonemgr; " + str(e)
-        self.processes[zonemgr.pid] = zonemgr 
-        if self.verbose:
-            sys.stdout.write("[bind10] Started b10-zonemgr(PID %d)\n" % 
-                             zonemgr.pid)
-
-        # start b10-stats
-        stats_args = ['b10-stats']
-        if self.verbose:
-            sys.stdout.write("[bind10] Starting b10-stats\n")
-            stats_args += ['-v']
-        try:
-            statsd = ProcessInfo("b10-stats", stats_args,
-                                 c_channel_env)
-        except Exception as e:
-            c_channel.process.kill()
-            bind_cfgd.process.kill()
-            xfrout.process.kill()
-            auth.process.kill()
-            xfrind.process.kill()
-            zonemgr.process.kill()
-            return "Unable to start b10-stats; " + str(e)
-
-        self.processes[statsd.pid] = statsd
-        if self.verbose:
-            sys.stdout.write("[bind10] Started b10-stats (PID %d)\n" % statsd.pid)
-
-        # start the b10-cmdctl
-        # XXX: we hardcode port 8080
-        cmdctl_args = ['b10-cmdctl']
-        if self.verbose:
-            sys.stdout.write("[bind10] Starting b10-cmdctl on port 8080\n")
-            cmdctl_args += ['-v']
-        try:
-            cmd_ctrld = ProcessInfo("b10-cmdctl", cmdctl_args,
-                                    c_channel_env)
-        except Exception as e:
-            c_channel.process.kill()
-            bind_cfgd.process.kill()
-            xfrout.process.kill()
-            auth.process.kill()
-            xfrind.process.kill()
-            zonemgr.process.kill()
-            statsd.process.kill()
-            return "Unable to start b10-cmdctl; " + str(e)
-        self.processes[cmd_ctrld.pid] = cmd_ctrld
-        if self.verbose:
-            sys.stdout.write("[bind10] Started b10-cmdctl (PID %d)\n" % 
-                             cmd_ctrld.pid)
-
+            self.kill_started_processes()
+            return "Unable to start " + self.curproc + ": " + str(e)
+
+        # Started successfully
         self.runnable = True
-
         return None
 
     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")
+        self.cc_session.group_sendmsg(cmd, "Recurse", "Recurse")
         self.cc_session.group_sendmsg(cmd, "Xfrout", "Xfrout")
         self.cc_session.group_sendmsg(cmd, "Xfrin", "Xfrin")
         self.cc_session.group_sendmsg(cmd, "Zonemgr", "Zonemgr")
-        self.cc_session.group_sendmsg(cmd, "Boss", "Stats")
+        self.cc_session.group_sendmsg(cmd, "Stats", "Stats")
 
     def stop_process(self, process):
         """Stop the given process, friendly-like."""
@@ -530,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:
@@ -567,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
@@ -615,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:
@@ -627,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:
@@ -642,22 +756,23 @@
     # Enforce line buffering on stdout, even when not a TTY
     sys.stdout = io.TextIOWrapper(sys.stdout.detach(), line_buffering=True)
 
-
     # Parse any command-line options.
     parser = OptionParser(version=VERSION)
     parser.add_option("-a", "--address", dest="address", type="string",
-                      action="callback", callback=check_addr, default='',
-                      help="address the b10-auth daemon will use (default: listen on all addresses)")
+                      action="callback", callback=check_addr, default=None,
+                      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")
@@ -702,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])
@@ -721,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)
@@ -781,6 +892,7 @@
     # shutdown
     signal.signal(signal.SIGCHLD, signal.SIG_DFL)
     boss_of_bind.shutdown()
+    sys.stdout.write("[bind10] BIND 10 exiting\n");
     sys.exit(0)
 
 if __name__ == "__main__":

Modified: branches/trac362/src/bin/bind10/bob.spec
==============================================================================
--- branches/trac362/src/bin/bind10/bob.spec (original)
+++ branches/trac362/src/bin/bind10/bob.spec Mon Dec 27 18:21:26 2010
@@ -3,6 +3,18 @@
     "module_name": "Boss",
     "module_description": "Master process",
     "config_data": [
+      {
+        "item_name": "start_auth",
+        "item_type": "boolean",
+        "item_optional": false,
+        "item_default": true
+      },
+      {
+        "item_name": "start_recurse",
+        "item_type": "boolean",
+        "item_optional": false,
+        "item_default": false
+      }
     ],
     "commands": [
       {

Modified: branches/trac362/src/bin/bind10/run_bind10.sh.in
==============================================================================
--- branches/trac362/src/bin/bind10/run_bind10.sh.in (original)
+++ branches/trac362/src/bin/bind10/run_bind10.sh.in Mon Dec 27 18:21:26 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:$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/trac362/src/bin/bind10/tests/bind10_test.py
==============================================================================
--- branches/trac362/src/bin/bind10/tests/bind10_test.py (original)
+++ branches/trac362/src/bin/bind10/tests/bind10_test.py Mon Dec 27 18:21:26 2010
@@ -78,44 +78,238 @@
         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.address, 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_socket(self):
         bob = BoB("alt_socket_file")
         self.assertEqual(bob.verbose, False)
         self.assertEqual(bob.msgq_socket_file, "alt_socket_file")
+        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)
-
-    def test_init_alternate_auth_port(self):
+        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.auth_port, 9999)
+        self.assertEqual(bob.dns_port, 9999)
+        self.assertEqual(bob.address, None)
         self.assertEqual(bob.cc_session, None)
-        self.assertEqual(bob.address, 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, 5300, IPAddr('127.127.127.127'))
+        bob = BoB(None, 1234, IPAddr('127.127.127.127'))
         self.assertEqual(bob.verbose, False)
-        self.assertEqual(bob.auth_port, 5300)
         self.assertEqual(bob.msgq_socket_file, None)
+        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.address.addr, socket.inet_aton('127.127.127.127'))
+        self.assertEqual(bob.ccs, None)
         self.assertEqual(bob.processes, {})
         self.assertEqual(bob.dead_processes, {})
         self.assertEqual(bob.runnable, False)
-    # verbose testing...
+        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)
+
+# Class for testing the Bob.start_all_processes() method call.
+#
+# Although testing that external processes start is outside the scope
+# of the unit test, by overriding the process start methods we can check
+# that the right processes are started depending on the configuration
+# options.
+class StartAllProcessesBob(BoB):
+    def __init__(self):
+        BoB.__init__(self)
+
+# Set flags as to which of the overridden methods has been run.
+        self.msgq = False
+        self.cfgmgr = False
+        self.ccsession = False
+        self.auth = False
+        self.recurse = False
+        self.xfrout = False
+        self.xfrin = False
+        self.zonemgr = False
+        self.stats = False
+        self.cmdctl = False
+
+    def read_bind10_config(self):
+        # Configuration options are set directly
+        pass
+
+    def start_msgq(self, c_channel_env):
+        self.msgq = True
+
+    def start_cfgmgr(self, c_channel_env):
+        self.cfgmgr = True
+
+    def start_ccsession(self, c_channel_env):
+        self.ccsession = True
+
+    def start_auth(self, c_channel_env):
+        self.auth = True
+
+    def start_recurse(self, c_channel_env):
+        self.recurse = True
+
+    def start_xfrout(self, c_channel_env):
+        self.xfrout = True
+
+    def start_xfrin(self, c_channel_env):
+        self.xfrin = True
+
+    def start_zonemgr(self, c_channel_env):
+        self.zonemgr = True
+
+    def start_stats(self, c_channel_env):
+        self.stats = True
+
+    def start_cmdctl(self, c_channel_env):
+        self.cmdctl = True
+
+# Check that the start_all_processes method starts the right combination
+# of processes.
+class TestStartAllProcessesBob(unittest.TestCase):
+    def check_preconditions(self, bob):
+        self.assertEqual(bob.msgq, False)
+        self.assertEqual(bob.cfgmgr, False)
+        self.assertEqual(bob.ccsession, False)
+        self.assertEqual(bob.auth, False)
+        self.assertEqual(bob.recurse, False)
+        self.assertEqual(bob.xfrout, False)
+        self.assertEqual(bob.xfrin, False)
+        self.assertEqual(bob.zonemgr, False)
+        self.assertEqual(bob.stats, False)
+        self.assertEqual(bob.cmdctl, False)
+
+    # Checks the processes started when starting neither auth nor recurse
+    # is specified.
+    def test_start_none(self):
+        # Created Bob and ensure initialization correct
+        bob = StartAllProcessesBob()
+        self.check_preconditions(bob)
+
+        # Start processes and check what was started
+        c_channel_env = {}
+        bob.cfg_start_auth = False
+        bob.cfg_start_recurse = False
+
+        bob.start_all_processes(c_channel_env)
+
+        self.assertEqual(bob.msgq, True)
+        self.assertEqual(bob.cfgmgr, True)
+        self.assertEqual(bob.ccsession, True)
+        self.assertEqual(bob.auth, False)
+        self.assertEqual(bob.recurse, False)
+        self.assertEqual(bob.xfrout, False)
+        self.assertEqual(bob.xfrin, False)
+        self.assertEqual(bob.zonemgr, False)
+        self.assertEqual(bob.stats, True)
+        self.assertEqual(bob.cmdctl, True)
+
+    # Checks the processes started when starting only the auth process
+    def test_start_auth(self):
+        # Created Bob and ensure initialization correct
+        bob = StartAllProcessesBob()
+        self.check_preconditions(bob)
+
+        # Start processes and check what was started
+        c_channel_env = {}
+        bob.cfg_start_auth = True
+        bob.cfg_start_recurse = False
+
+        bob.start_all_processes(c_channel_env)
+
+        self.assertEqual(bob.msgq, True)
+        self.assertEqual(bob.cfgmgr, True)
+        self.assertEqual(bob.ccsession, True)
+        self.assertEqual(bob.auth, True)
+        self.assertEqual(bob.recurse, False)
+        self.assertEqual(bob.xfrout, True)
+        self.assertEqual(bob.xfrin, True)
+        self.assertEqual(bob.zonemgr, True)
+        self.assertEqual(bob.stats, True)
+        self.assertEqual(bob.cmdctl, True)
+
+    # Checks the processes started when starting only the recurse process
+    def test_start_recurse(self):
+        # Created Bob and ensure initialization correct
+        bob = StartAllProcessesBob()
+        self.check_preconditions(bob)
+
+        # Start processes and check what was started
+        c_channel_env = {}
+        bob.cfg_start_auth = False
+        bob.cfg_start_recurse = True
+
+        bob.start_all_processes(c_channel_env)
+
+        self.assertEqual(bob.msgq, True)
+        self.assertEqual(bob.cfgmgr, True)
+        self.assertEqual(bob.ccsession, True)
+        self.assertEqual(bob.auth, False)
+        self.assertEqual(bob.recurse, True)
+        self.assertEqual(bob.xfrout, False)
+        self.assertEqual(bob.xfrin, False)
+        self.assertEqual(bob.zonemgr, False)
+        self.assertEqual(bob.stats, True)
+        self.assertEqual(bob.cmdctl, True)
+
+    # Checks the processes started when starting both auth and recurse process
+    def test_start_both(self):
+        # Created Bob and ensure initialization correct
+        bob = StartAllProcessesBob()
+        self.check_preconditions(bob)
+
+        # Start processes and check what was started
+        c_channel_env = {}
+        bob.cfg_start_auth = True
+        bob.cfg_start_recurse = True
+
+        bob.start_all_processes(c_channel_env)
+
+        self.assertEqual(bob.msgq, True)
+        self.assertEqual(bob.cfgmgr, True)
+        self.assertEqual(bob.ccsession, True)
+        self.assertEqual(bob.auth, True)
+        self.assertEqual(bob.recurse, True)
+        self.assertEqual(bob.xfrout, True)
+        self.assertEqual(bob.xfrin, True)
+        self.assertEqual(bob.zonemgr, True)
+        self.assertEqual(bob.stats, True)
+        self.assertEqual(bob.cmdctl, True)
+
 
 if __name__ == '__main__':
     unittest.main()

Modified: branches/trac362/src/bin/bindctl/bindcmd.py
==============================================================================
--- branches/trac362/src/bin/bindctl/bindcmd.py (original)
+++ branches/trac362/src/bin/bindctl/bindcmd.py Mon Dec 27 18:21:26 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/trac362/src/bin/bindctl/bindctl-source.py.in
==============================================================================
--- branches/trac362/src/bin/bindctl/bindctl-source.py.in (original)
+++ branches/trac362/src/bin/bindctl/bindctl-source.py.in Mon Dec 27 18:21:26 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/trac362/src/bin/bindctl/run_bindctl.sh.in
==============================================================================
--- branches/trac362/src/bin/bindctl/run_bindctl.sh.in (original)
+++ branches/trac362/src/bin/bindctl/run_bindctl.sh.in Mon Dec 27 18:21:26 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/trac362/src/bin/cmdctl/run_b10-cmdctl.sh.in
==============================================================================
--- branches/trac362/src/bin/cmdctl/run_b10-cmdctl.sh.in (original)
+++ branches/trac362/src/bin/cmdctl/run_b10-cmdctl.sh.in Mon Dec 27 18:21:26 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/trac362/src/bin/loadzone/run_loadzone.sh.in
==============================================================================
--- branches/trac362/src/bin/loadzone/run_loadzone.sh.in (original)
+++ branches/trac362/src/bin/loadzone/run_loadzone.sh.in Mon Dec 27 18:21:26 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/trac362/src/bin/msgq/msgq.py.in
==============================================================================
--- branches/trac362/src/bin/msgq/msgq.py.in (original)
+++ branches/trac362/src/bin/msgq/msgq.py.in Mon Dec 27 18:21:26 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/trac362/src/bin/msgq/run_msgq.sh.in
==============================================================================
--- branches/trac362/src/bin/msgq/run_msgq.sh.in (original)
+++ branches/trac362/src/bin/msgq/run_msgq.sh.in Mon Dec 27 18:21:26 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/trac362/src/bin/stats/run_b10-stats.sh.in
==============================================================================
--- branches/trac362/src/bin/stats/run_b10-stats.sh.in (original)
+++ branches/trac362/src/bin/stats/run_b10-stats.sh.in Mon Dec 27 18:21:26 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/trac362/src/bin/stats/run_b10-stats_stub.sh.in
==============================================================================
--- branches/trac362/src/bin/stats/run_b10-stats_stub.sh.in (original)
+++ branches/trac362/src/bin/stats/run_b10-stats_stub.sh.in Mon Dec 27 18:21:26 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/trac362/src/bin/usermgr/run_b10-cmdctl-usermgr.sh.in
==============================================================================
--- branches/trac362/src/bin/usermgr/run_b10-cmdctl-usermgr.sh.in (original)
+++ branches/trac362/src/bin/usermgr/run_b10-cmdctl-usermgr.sh.in Mon Dec 27 18:21:26 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/trac362/src/bin/xfrin/b10-xfrin.xml
==============================================================================
--- branches/trac362/src/bin/xfrin/b10-xfrin.xml (original)
+++ branches/trac362/src/bin/xfrin/b10-xfrin.xml Mon Dec 27 18:21:26 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/trac362/src/bin/xfrin/run_b10-xfrin.sh.in
==============================================================================
--- branches/trac362/src/bin/xfrin/run_b10-xfrin.sh.in (original)
+++ branches/trac362/src/bin/xfrin/run_b10-xfrin.sh.in Mon Dec 27 18:21:26 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/trac362/src/bin/xfrout/b10-xfrout.8
==============================================================================
--- branches/trac362/src/bin/xfrout/b10-xfrout.8 (original)
+++ branches/trac362/src/bin/xfrout/b10-xfrout.8 Mon Dec 27 18:21:26 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/trac362/src/bin/xfrout/b10-xfrout.xml
==============================================================================
--- branches/trac362/src/bin/xfrout/b10-xfrout.xml (original)
+++ branches/trac362/src/bin/xfrout/b10-xfrout.xml Mon Dec 27 18:21:26 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/trac362/src/bin/xfrout/run_b10-xfrout.sh.in
==============================================================================
--- branches/trac362/src/bin/xfrout/run_b10-xfrout.sh.in (original)
+++ branches/trac362/src/bin/xfrout/run_b10-xfrout.sh.in Mon Dec 27 18:21:26 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/trac362/src/bin/xfrout/xfrout.spec.pre.in
==============================================================================
--- branches/trac362/src/bin/xfrout/xfrout.spec.pre.in (original)
+++ branches/trac362/src/bin/xfrout/xfrout.spec.pre.in Mon Dec 27 18:21:26 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/trac362/src/bin/zonemgr/run_b10-zonemgr.sh.in
==============================================================================
--- branches/trac362/src/bin/zonemgr/run_b10-zonemgr.sh.in (original)
+++ branches/trac362/src/bin/zonemgr/run_b10-zonemgr.sh.in Mon Dec 27 18:21:26 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/trac362/src/lib/Makefile.am
==============================================================================
--- branches/trac362/src/lib/Makefile.am (original)
+++ branches/trac362/src/lib/Makefile.am Mon Dec 27 18:21:26 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/trac362/src/lib/cc/session.cc
==============================================================================
--- branches/trac362/src/lib/cc/session.cc (original)
+++ branches/trac362/src/lib/cc/session.cc Mon Dec 27 18:21:26 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/trac362/src/lib/config/config_data.cc
==============================================================================
--- branches/trac362/src/lib/config/config_data.cc (original)
+++ branches/trac362/src/lib/config/config_data.cc Mon Dec 27 18:21:26 2010
@@ -128,11 +128,6 @@
                 if (recurse && list_el->get("item_type")->stringValue() == "map") {
                     spec_name_list(result, list_el->get("map_item_spec"), new_prefix, recurse);
                 } else {
-                    if (list_el->get("item_type")->stringValue() == "map" ||
-                        list_el->get("item_type")->stringValue() == "list"
-                    ) {
-                        new_prefix += "/";
-                    }
                     result->add(Element::create(new_prefix));
                 }
             }

Modified: branches/trac362/src/lib/config/module_spec.cc
==============================================================================
--- branches/trac362/src/lib/config/module_spec.cc (original)
+++ branches/trac362/src/lib/config/module_spec.cc Mon Dec 27 18:21:26 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/trac362/src/lib/config/tests/config_data_unittests.cc
==============================================================================
--- branches/trac362/src/lib/config/tests/config_data_unittests.cc (original)
+++ branches/trac362/src/lib/config/tests/config_data_unittests.cc Mon Dec 27 18:21:26 2010
@@ -120,8 +120,8 @@
     ModuleSpec spec2 = moduleSpecFromFile(std::string(TEST_DATA_PATH) + "/spec2.spec");
     ConfigData cd = ConfigData(spec2);
 
-    EXPECT_EQ("[ \"item1\", \"item2\", \"item3\", \"item4\", \"item5/\", \"item6/\" ]", cd.getItemList()->str());
-    EXPECT_EQ("[ \"item1\", \"item2\", \"item3\", \"item4\", \"item5/\", \"item6/value1\", \"item6/value2\" ]", cd.getItemList("", true)->str());
+    EXPECT_EQ("[ \"item1\", \"item2\", \"item3\", \"item4\", \"item5\", \"item6\" ]", cd.getItemList()->str());
+    EXPECT_EQ("[ \"item1\", \"item2\", \"item3\", \"item4\", \"item5\", \"item6/value1\", \"item6/value2\" ]", cd.getItemList("", true)->str());
     EXPECT_EQ("[ \"item6/value1\", \"item6/value2\" ]", cd.getItemList("item6")->str());
 }
 
@@ -129,12 +129,12 @@
     ModuleSpec spec2 = moduleSpecFromFile(std::string(TEST_DATA_PATH) + "/spec2.spec");
     ConfigData cd = ConfigData(spec2);
 
-    EXPECT_EQ("{ \"item1\": 1, \"item2\": 1.1, \"item3\": true, \"item4\": \"test\", \"item5/\": [ \"a\", \"b\" ], \"item6/value1\": \"default\", \"item6/value2\": None }", cd.getFullConfig()->str());
+    EXPECT_EQ("{ \"item1\": 1, \"item2\": 1.1, \"item3\": true, \"item4\": \"test\", \"item5\": [ \"a\", \"b\" ], \"item6/value1\": \"default\", \"item6/value2\": None }", cd.getFullConfig()->str());
     ElementPtr my_config = Element::fromJSON("{ \"item1\": 2 }");
     cd.setLocalConfig(my_config);
-    EXPECT_EQ("{ \"item1\": 2, \"item2\": 1.1, \"item3\": true, \"item4\": \"test\", \"item5/\": [ \"a\", \"b\" ], \"item6/value1\": \"default\", \"item6/value2\": None }", cd.getFullConfig()->str());
+    EXPECT_EQ("{ \"item1\": 2, \"item2\": 1.1, \"item3\": true, \"item4\": \"test\", \"item5\": [ \"a\", \"b\" ], \"item6/value1\": \"default\", \"item6/value2\": None }", cd.getFullConfig()->str());
     ElementPtr my_config2 = Element::fromJSON("{ \"item6\": { \"value1\": \"a\" } }");
     cd.setLocalConfig(my_config2);
-    EXPECT_EQ("{ \"item1\": 1, \"item2\": 1.1, \"item3\": true, \"item4\": \"test\", \"item5/\": [ \"a\", \"b\" ], \"item6/value1\": \"a\", \"item6/value2\": None }", cd.getFullConfig()->str());
+    EXPECT_EQ("{ \"item1\": 1, \"item2\": 1.1, \"item3\": true, \"item4\": \"test\", \"item5\": [ \"a\", \"b\" ], \"item6/value1\": \"a\", \"item6/value2\": None }", cd.getFullConfig()->str());
 }
 

Modified: branches/trac362/src/lib/config/tests/module_spec_unittests.cc
==============================================================================
--- branches/trac362/src/lib/config/tests/module_spec_unittests.cc (original)
+++ branches/trac362/src/lib/config/tests/module_spec_unittests.cc Mon Dec 27 18:21:26 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/trac362/src/lib/config/tests/testdata/Makefile.am
==============================================================================
--- branches/trac362/src/lib/config/tests/testdata/Makefile.am (original)
+++ branches/trac362/src/lib/config/tests/testdata/Makefile.am Mon Dec 27 18:21:26 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/trac362/src/lib/config/tests/testdata/data22_8.data
==============================================================================
--- branches/trac362/src/lib/config/tests/testdata/data22_8.data (original)
+++ branches/trac362/src/lib/config/tests/testdata/data22_8.data Mon Dec 27 18:21:26 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/trac362/src/lib/datasrc/Makefile.am
==============================================================================
--- branches/trac362/src/lib/datasrc/Makefile.am (original)
+++ branches/trac362/src/lib/datasrc/Makefile.am Mon Dec 27 18:21:26 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/trac362/src/lib/datasrc/data_source.h
==============================================================================
--- branches/trac362/src/lib/datasrc/data_source.h (original)
+++ branches/trac362/src/lib/datasrc/data_source.h Mon Dec 27 18:21:26 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/trac362/src/lib/datasrc/static_datasrc.cc
==============================================================================
--- branches/trac362/src/lib/datasrc/static_datasrc.cc (original)
+++ branches/trac362/src/lib/datasrc/static_datasrc.cc Mon Dec 27 18:21:26 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/trac362/src/lib/datasrc/tests/Makefile.am
==============================================================================
--- branches/trac362/src/lib/datasrc/tests/Makefile.am (original)
+++ branches/trac362/src/lib/datasrc/tests/Makefile.am Mon Dec 27 18:21:26 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/trac362/src/lib/datasrc/tests/datasrc_unittest.cc
==============================================================================
--- branches/trac362/src/lib/datasrc/tests/datasrc_unittest.cc (original)
+++ branches/trac362/src/lib/datasrc/tests/datasrc_unittest.cc Mon Dec 27 18:21:26 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/trac362/src/lib/datasrc/tests/static_unittest.cc
==============================================================================
--- branches/trac362/src/lib/datasrc/tests/static_unittest.cc (original)
+++ branches/trac362/src/lib/datasrc/tests/static_unittest.cc Mon Dec 27 18:21:26 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/trac362/src/lib/datasrc/tests/zonetable_unittest.cc
==============================================================================
--- branches/trac362/src/lib/datasrc/tests/zonetable_unittest.cc (original)
+++ branches/trac362/src/lib/datasrc/tests/zonetable_unittest.cc Mon Dec 27 18:21:26 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/trac362/src/lib/datasrc/zonetable.cc
==============================================================================
--- branches/trac362/src/lib/datasrc/zonetable.cc (original)
+++ branches/trac362/src/lib/datasrc/zonetable.cc Mon Dec 27 18:21:26 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,79 @@
 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);
+                // Because of warning
+                return (FindResult(result::NOTFOUND, ConstZonePtr()));
+        }
+
+        // Can Not Happen (remember, NOTFOUND is handled)
+        assert(node);
+
+        return (FindResult(my_result, node->getData()));
+    }
 };
 
 ZoneTable::ZoneTable() : impl_(new ZoneTableImpl)
@@ -70,41 +107,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/trac362/src/lib/datasrc/zonetable.h
==============================================================================
--- branches/trac362/src/lib/datasrc/zonetable.h (original)
+++ branches/trac362/src/lib/datasrc/zonetable.h Mon Dec 27 18:21:26 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/trac362/src/lib/dns/Makefile.am
==============================================================================
--- branches/trac362/src/lib/dns/Makefile.am (original)
+++ branches/trac362/src/lib/dns/Makefile.am Mon Dec 27 18:21:26 2010
@@ -13,6 +13,8 @@
 
 # TODO: double-check that this is the only way
 # NOTE: when an rdata file is added, please also add to this list:
+EXTRA_DIST += rdata/any_255/tsig_250.cc
+EXTRA_DIST += rdata/any_255/tsig_250.h
 EXTRA_DIST += rdata/in_1/aaaa_28.cc
 EXTRA_DIST += rdata/in_1/aaaa_28.h
 EXTRA_DIST += rdata/in_1/a_1.cc
@@ -67,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
@@ -96,11 +99,13 @@
 libdns___include_HEADERS = \
 	buffer.h \
 	dnssectime.h \
+	edns.h \
 	exceptions.h \
 	message.h \
 	messagerenderer.h \
 	name.h \
 	question.h \
+	rcode.h \
 	rdata.h \
 	rdataclass.h \
 	rrclass.h \

Modified: branches/trac362/src/lib/dns/buffer.h
==============================================================================
--- branches/trac362/src/lib/dns/buffer.h (original)
+++ branches/trac362/src/lib/dns/buffer.h Mon Dec 27 18:21:26 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/trac362/src/lib/dns/message.h
==============================================================================
--- branches/trac362/src/lib/dns/message.h (original)
+++ branches/trac362/src/lib/dns/message.h Mon Dec 27 18:21:26 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/trac362/src/lib/dns/messagerenderer.h
==============================================================================
--- branches/trac362/src/lib/dns/messagerenderer.h (original)
+++ branches/trac362/src/lib/dns/messagerenderer.h Mon Dec 27 18:21:26 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/trac362/src/lib/dns/rrclass-placeholder.h
==============================================================================
--- branches/trac362/src/lib/dns/rrclass-placeholder.h (original)
+++ branches/trac362/src/lib/dns/rrclass-placeholder.h Mon Dec 27 18:21:26 2010
@@ -244,14 +244,12 @@
     // END_WELL_KNOWN_CLASS_DECLARATIONS
     
     static const RRClass& NONE();
-    static const RRClass& ANY();
 
 private:
     // \brief Meta-classes
     enum {
         RRCLASS_RESERVED0 = 0,
-        RRCLASS_NONE = 254,
-        RRCLASS_ANY = 255
+        RRCLASS_NONE = 254
     };
     uint16_t classcode_;
 };
@@ -262,13 +260,6 @@
 inline const RRClass&
 RRClass::NONE() {
     static RRClass rrclass(RRCLASS_NONE);
-
-    return (rrclass);
-}
-
-inline const RRClass&
-RRClass::ANY() {
-    static RRClass rrclass(RRCLASS_ANY);
 
     return (rrclass);
 }

Modified: branches/trac362/src/lib/dns/tests/Makefile.am
==============================================================================
--- branches/trac362/src/lib/dns/tests/Makefile.am (original)
+++ branches/trac362/src/lib/dns/tests/Makefile.am Mon Dec 27 18:21:26 2010
@@ -38,9 +38,11 @@
 run_unittests_SOURCES += rdata_nsec3_unittest.cc
 run_unittests_SOURCES += rdata_nsec3param_unittest.cc
 run_unittests_SOURCES += rdata_rrsig_unittest.cc
+run_unittests_SOURCES += rdata_tsig_unittest.cc
 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/trac362/src/lib/dns/tests/testdata/Makefile.am
==============================================================================
--- branches/trac362/src/lib/dns/tests/testdata/Makefile.am (original)
+++ branches/trac362/src/lib/dns/tests/testdata/Makefile.am Mon Dec 27 18:21:26 2010
@@ -12,12 +12,21 @@
 BUILT_SOURCES += rdata_soa_toWireUncompressed.wire
 BUILT_SOURCES +=  rdata_txt_fromWire2.wire rdata_txt_fromWire3.wire
 BUILT_SOURCES += rdata_txt_fromWire4.wire rdata_txt_fromWire5.wire
+BUILT_SOURCES += rdata_tsig_fromWire1.wire rdata_tsig_fromWire2.wire
+BUILT_SOURCES += rdata_tsig_fromWire3.wire rdata_tsig_fromWire4.wire
+BUILT_SOURCES += rdata_tsig_fromWire5.wire rdata_tsig_fromWire6.wire
+BUILT_SOURCES += rdata_tsig_fromWire7.wire rdata_tsig_fromWire8.wire
+BUILT_SOURCES += rdata_tsig_fromWire9.wire
+BUILT_SOURCES += rdata_tsig_toWire1.wire rdata_tsig_toWire2.wire
+BUILT_SOURCES += rdata_tsig_toWire3.wire rdata_tsig_toWire4.wire
+BUILT_SOURCES += rdata_tsig_toWire5.wire
 
 # NOTE: keep this in sync with real file listing
 # so is included in tarball
 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
@@ -51,6 +60,14 @@
 EXTRA_DIST += rrcode16_fromWire1 rrcode16_fromWire2
 EXTRA_DIST += rrcode32_fromWire1 rrcode32_fromWire2
 EXTRA_DIST += rrset_toWire1 rrset_toWire2
+EXTRA_DIST += rdata_tsig_fromWire1.spec rdata_tsig_fromWire2.spec
+EXTRA_DIST += rdata_tsig_fromWire3.spec rdata_tsig_fromWire4.spec
+EXTRA_DIST += rdata_tsig_fromWire5.spec rdata_tsig_fromWire6.spec
+EXTRA_DIST += rdata_tsig_fromWire7.spec rdata_tsig_fromWire8.spec
+EXTRA_DIST += rdata_tsig_fromWire9.spec
+EXTRA_DIST += rdata_tsig_toWire1.spec rdata_tsig_toWire2.spec
+EXTRA_DIST += rdata_tsig_toWire3.spec rdata_tsig_toWire4.spec
+EXTRA_DIST += rdata_tsig_toWire5.spec
 
 .spec.wire:
 	./gen-wiredata.py -o $@ $<

Modified: branches/trac362/src/lib/dns/tests/testdata/edns_toWire4.spec
==============================================================================
--- branches/trac362/src/lib/dns/tests/testdata/edns_toWire4.spec (original)
+++ branches/trac362/src/lib/dns/tests/testdata/edns_toWire4.spec Mon Dec 27 18:21:26 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/trac362/src/lib/dns/tests/testdata/gen-wiredata.py.in
==============================================================================
--- branches/trac362/src/lib/dns/tests/testdata/gen-wiredata.py.in (original)
+++ branches/trac362/src/lib/dns/tests/testdata/gen-wiredata.py.in Mon Dec 27 18:21:26 2010
@@ -19,8 +19,8 @@
 from datetime import datetime
 from optparse import OptionParser
 
-re_hex = re.compile(r'0x[0-9a-fA-F]+')
-re_decimal = re.compile(r'\d+$')
+re_hex = re.compile(r'^0x[0-9a-fA-F]+')
+re_decimal = re.compile(r'^\d+$')
 re_string = re.compile(r"\'(.*)\'$")
 
 dnssec_timefmt = '%Y%m%d%H%M%S'
@@ -48,9 +48,12 @@
                 'maila' : 254, 'any' : 255 }
 rdict_rrtype = dict([(dict_rrtype[k], k.upper()) for k in dict_rrtype.keys()])
 dict_rrclass = { 'in' : 1, 'ch' : 3, 'hs' : 4, 'any' : 255 }
-rdict_rrclass = dict([(dict_rrclass[k], k.upper()) for k in dict_rrclass.keys()])
-dict_algorithm = { 'rsamd5' : 1, 'dh' : 2, 'dsa' : 3, 'ecc' : 4, 'rsasha1' : 5 }
-rdict_algorithm = dict([(dict_algorithm[k], k.upper()) for k in dict_algorithm.keys()])
+rdict_rrclass = dict([(dict_rrclass[k], k.upper()) for k in \
+                          dict_rrclass.keys()])
+dict_algorithm = { 'rsamd5' : 1, 'dh' : 2, 'dsa' : 3, 'ecc' : 4,
+                   'rsasha1' : 5 }
+rdict_algorithm = dict([(dict_algorithm[k], k.upper()) for k in \
+                            dict_algorithm.keys()])
 
 header_xtables = { 'qr' : dict_qr, 'opcode' : dict_opcode,
                    'rcode' : dict_rcode }
@@ -75,13 +78,17 @@
         return dict[code] + '(' + str(code) + ')'
     return str(code)
 
-def encode_name(name, absolute = True):
+def encode_name(name, absolute=True):
     # make sure the name is dot-terminated.  duplicate dots will be ignored
     # below.
     name += '.'
     labels = name.split('.')
     wire = ''
     for l in labels:
+        if len(l) > 4 and l[0:4] == 'ptr=':
+            # special meta-syntax for compression pointer
+            wire += ' %04x' % (0xc000 | int(l[4:]))
+            break
         if absolute or len(l) > 0:
             wire += '%02x' % len(l)
             wire += ''.join(['%02x' % ord(ch) for ch in l])
@@ -89,7 +96,9 @@
             break
     return wire
 
-def encode_string(name):
+def encode_string(name, len=None):
+    if type(name) is int and len is not None:
+        return '%0.*x' % (len * 2, name)
     return ''.join(['%02x' % ord(ch) for ch in name])
 
 def count_namelabels(name):
@@ -121,17 +130,19 @@
 
 class Name:
     name = 'example.com'
-    pointer = -1                # no compression by default
-    def dump(self, f):
-        name_wire = encode_name(self.name,
-                                True if self.pointer == -1 else False)
+    pointer = None                # no compression by default
+    def dump(self, f):
+        name = self.name
+        if self.pointer is not None:
+            if len(name) > 0 and name[-1] != '.':
+                name += '.'
+            name += 'ptr=%d' % self.pointer
+        name_wire = encode_name(name)
         f.write('\n# DNS Name: %s' % self.name)
-        if self.pointer >= 0:
+        if self.pointer is not None:
             f.write(' + compression pointer: %d' % self.pointer)
         f.write('\n')
         f.write('%s' % name_wire)
-        if self.pointer >= 0:
-            f.write(' %04x' % (0xc000 | self.pointer))
         f.write('\n')
 
 class DNSHeader:
@@ -338,20 +349,73 @@
                 (code_totext(self.covered, rdict_rrtype),
                  code_totext(self.algorithm, rdict_algorithm), labels,
                  self.originalttl))
-        f.write('%04x %02x %02x %08x\n' % (self.covered, self.algorithm, labels,
-                                           self.originalttl))
+        f.write('%04x %02x %02x %08x\n' % (self.covered, self.algorithm,
+                                           labels, self.originalttl))
         f.write('# Expiration=%s, Inception=%s\n' %
                 (str(self.expiration), str(self.inception)))
         f.write('%08x %08x\n' % (self.expiration, self.inception))
         f.write('# Tag=%d Signer=%s and Signature\n' % (self.tag, self.signer))
         f.write('%04x %s %s\n' % (self.tag, name_wire, sig_wire))
 
+class TSIG:
+    rdlen = None                # auto-calculate
+    algorithm = 'hmac-sha256'
+    time_signed = 1286978795    # arbitrarily chosen default
+    fudge = 300
+    mac_size = None             # use a common value for the algorithm
+    mac = None                  # use 'x' * mac_size
+    original_id = 2845          # arbitrarily chosen default
+    error = 0
+    other_len = None         # 6 if error is BADTIME; otherwise 0
+    other_data = None        # use time_signed + fudge + 1 for BADTIME
+    dict_macsize = { 'hmac-md5' : 16, 'hmac-sha1' : 20, 'hmac-sha256' : 32 }
+    def dump(self, f):
+        if str(self.algorithm) == 'hmac-md5':
+            name_wire = encode_name('hmac-md5.sig-alg.reg.int')
+        else:
+            name_wire = encode_name(self.algorithm)
+        rdlen = self.rdlen
+        mac_size = self.mac_size
+        if mac_size is None:
+            if self.algorithm in self.dict_macsize.keys():
+                mac_size = self.dict_macsize[self.algorithm]
+            else:
+                raise RuntimeError('TSIG Mac Size cannot be determined')
+        mac = encode_string('x' * mac_size) if self.mac is None else \
+            encode_string(self.mac, mac_size)
+        other_len = self.other_len
+        if other_len is None:
+            # 18 = BADTIME
+            other_len = 6 if self.error == 18 else 0
+        other_data = self.other_data
+        if other_data is None:
+            other_data = '%012x' % (self.time_signed + self.fudge + 1) \
+                if self.error == 18 else ''
+        else:
+            other_data = encode_string(self.other_data, other_len)
+        if rdlen is None:
+            rdlen = int(len(name_wire) / 2 + 16 + len(mac) / 2 + \
+                            len(other_data) / 2)
+        f.write('\n# TSIG RDATA (RDLEN=%d)\n' % rdlen)
+        f.write('%04x\n' % rdlen);
+        f.write('# Algorithm=%s Time-Signed=%d Fudge=%d\n' %
+                (self.algorithm, self.time_signed, self.fudge))
+        f.write('%s %012x %04x\n' % (name_wire, self.time_signed, self.fudge))
+        f.write('# MAC Size=%d MAC=(see hex)\n' % mac_size)
+        f.write('%04x%s\n' % (mac_size, ' ' + mac if len(mac) > 0 else ''))
+        f.write('# Original-ID=%d Error=%d\n' % (self.original_id, self.error))
+        f.write('%04x %04x\n' %  (self.original_id, self.error))
+        f.write('# Other-Len=%d Other-Data=(see hex)\n' % other_len)
+        f.write('%04x%s\n' % (other_len,
+                              ' ' + other_data if len(other_data) > 0 else ''))
+
 def get_config_param(section):
     config_param = {'name' : (Name, {}),
                     'header' : (DNSHeader, header_xtables),
                     'question' : (DNSQuestion, question_xtables),
                     'edns' : (EDNS, {}), 'soa' : (SOA, {}), 'txt' : (TXT, {}),
-                    'rrsig' : (RRSIG, {}), 'nsec' : (NSEC, {})}
+                    'rrsig' : (RRSIG, {}), 'nsec' : (NSEC, {}),
+                    'tsig' : (TSIG, {}) }
     s = section
     m = re.match('^([^:]+)/\d+$', section)
     if m:

Modified: branches/trac362/src/lib/dns/tests/tsigkey_unittest.cc
==============================================================================
--- branches/trac362/src/lib/dns/tests/tsigkey_unittest.cc (original)
+++ branches/trac362/src/lib/dns/tests/tsigkey_unittest.cc Mon Dec 27 18:21:26 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/trac362/src/lib/dns/tests/unittest_util.cc
==============================================================================
--- branches/trac362/src/lib/dns/tests/unittest_util.cc (original)
+++ branches/trac362/src/lib/dns/tests/unittest_util.cc Mon Dec 27 18:21:26 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/trac362/src/lib/dns/tests/unittest_util.h
==============================================================================
--- branches/trac362/src/lib/dns/tests/unittest_util.h (original)
+++ branches/trac362/src/lib/dns/tests/unittest_util.h Mon Dec 27 18:21:26 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/trac362/src/lib/python/isc/cc/data.py
==============================================================================
--- branches/trac362/src/lib/python/isc/cc/data.py (original)
+++ branches/trac362/src/lib/python/isc/cc/data.py Mon Dec 27 18:21:26 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/trac362/src/lib/python/isc/cc/tests/data_test.py
==============================================================================
--- branches/trac362/src/lib/python/isc/cc/tests/data_test.py (original)
+++ branches/trac362/src/lib/python/isc/cc/tests/data_test.py Mon Dec 27 18:21:26 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/trac362/src/lib/python/isc/config/ccsession.py
==============================================================================
--- branches/trac362/src/lib/python/isc/config/ccsession.py (original)
+++ branches/trac362/src/lib/python/isc/config/ccsession.py Mon Dec 27 18:21:26 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/trac362/src/lib/python/isc/config/config_data.py
==============================================================================
--- branches/trac362/src/lib/python/isc/config/config_data.py (original)
+++ branches/trac362/src/lib/python/isc/config/config_data.py Mon Dec 27 18:21:26 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']:
@@ -158,11 +170,9 @@
                     result.extend(spec_name_list(list_el['map_item_spec'], prefix + list_el['item_name'], recurse))
                 else:
                     name = list_el['item_name']
-                    if list_el['item_type'] in ["list", "map"]:
-                        name += "/"
                     result.append(prefix + name)
             else:
-                raise ConfigDataError("Bad specication")
+                raise ConfigDataError("Bad specification")
     else:
         raise ConfigDataError("Bad specication")
     return result
@@ -228,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
@@ -272,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:]
@@ -336,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):
@@ -374,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] == '/':
@@ -389,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):
@@ -441,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/trac362/src/lib/python/isc/config/module_spec.py
==============================================================================
--- branches/trac362/src/lib/python/isc/config/module_spec.py (original)
+++ branches/trac362/src/lib/python/isc/config/module_spec.py Mon Dec 27 18:21:26 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/trac362/src/lib/python/isc/config/tests/ccsession_test.py
==============================================================================
--- branches/trac362/src/lib/python/isc/config/tests/ccsession_test.py (original)
+++ branches/trac362/src/lib/python/isc/config/tests/ccsession_test.py Mon Dec 27 18:21:26 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/trac362/src/lib/python/isc/config/tests/config_data_test.py
==============================================================================
--- branches/trac362/src/lib/python/isc/config/tests/config_data_test.py (original)
+++ branches/trac362/src/lib/python/isc/config/tests/config_data_test.py Mon Dec 27 18:21:26 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 })
@@ -175,9 +189,9 @@
 
     def test_spec_name_list(self):
         name_list = spec_name_list(self.cd.get_module_spec().get_config_spec())
-        self.assertEqual(['item1', 'item2', 'item3', 'item4', 'item5/', 'item6/'], name_list)
+        self.assertEqual(['item1', 'item2', 'item3', 'item4', 'item5', 'item6'], name_list)
         name_list = spec_name_list(self.cd.get_module_spec().get_config_spec(), "", True)
-        self.assertEqual(['item1', 'item2', 'item3', 'item4', 'item5/', 'item6/value1', 'item6/value2'], name_list)
+        self.assertEqual(['item1', 'item2', 'item3', 'item4', 'item5', 'item6/value1', 'item6/value2'], name_list)
         spec_part = find_spec_part(self.cd.get_module_spec().get_config_spec(), "item6")
         name_list = spec_name_list(spec_part, "item6", True)
         self.assertEqual(['item6/value1', 'item6/value2'], name_list)
@@ -193,7 +207,7 @@
         name_list = spec_name_list({ "myModule": config_spec }, "", False)
         self.assertEqual(['myModule/'], name_list)
         name_list = spec_name_list({ "myModule": config_spec }, "", True)
-        self.assertEqual(['myModule/', 'myModule/value1', 'myModule/value2', 'myModule/value3', 'myModule/value4', 'myModule/value5/', 'myModule/value6/v61', 'myModule/value6/v62', 'myModule/value7/', 'myModule/value8/', 'myModule/value9/v91', 'myModule/value9/v92/v92a', 'myModule/value9/v92/v92b'], name_list)
+        self.assertEqual(['myModule/', 'myModule/value1', 'myModule/value2', 'myModule/value3', 'myModule/value4', 'myModule/value5', 'myModule/value6/v61', 'myModule/value6/v62', 'myModule/value7', 'myModule/value8', 'myModule/value9/v91', 'myModule/value9/v92/v92a', 'myModule/value9/v92/v92b'], name_list)
 
         self.assertRaises(ConfigDataError, spec_name_list, 1)
         self.assertRaises(ConfigDataError, spec_name_list, [ 'a' ])
@@ -240,19 +254,19 @@
 
     def test_get_item_list(self):
         name_list = self.cd.get_item_list()
-        self.assertEqual(['item1', 'item2', 'item3', 'item4', 'item5/', 'item6/'], name_list)
+        self.assertEqual(['item1', 'item2', 'item3', 'item4', 'item5', 'item6'], name_list)
         name_list = self.cd.get_item_list("", True)
-        self.assertEqual(['item1', 'item2', 'item3', 'item4', 'item5/', 'item6/value1', 'item6/value2'], name_list)
+        self.assertEqual(['item1', 'item2', 'item3', 'item4', 'item5', 'item6/value1', 'item6/value2'], name_list)
         name_list = self.cd.get_item_list("item6", False)
         self.assertEqual(['item6/value1', 'item6/value2'], name_list)
 
     def test_get_full_config(self):
         full_config = self.cd.get_full_config()
-        self.assertEqual({ "item1": 1, "item2": 1.1, "item3": True, "item4": "test", "item5/": ['a', 'b'], "item6/value1": 'default', 'item6/value2': None}, full_config)
+        self.assertEqual({ "item1": 1, "item2": 1.1, "item3": True, "item4": "test", "item5": ['a', 'b'], "item6/value1": 'default', 'item6/value2': None}, full_config)
         my_config = { "item1": 2, "item2": 2.2, "item3": False, "item4": "asdf", "item5": [ "c", "d" ] }
         self.cd.set_local_config(my_config)
         full_config = self.cd.get_full_config()
-        self.assertEqual({ "item1": 2, "item2": 2.2, "item3": False, "item4": "asdf", "item5/": [ "c", "d" ], "item6/value1": 'default', 'item6/value2': None}, full_config)
+        self.assertEqual({ "item1": 2, "item2": 2.2, "item3": False, "item4": "asdf", "item5": [ "c", "d" ], "item6/value1": 'default', 'item6/value2': None}, full_config)
 
 class TestMultiConfigData(unittest.TestCase):
     def setUp(self):
@@ -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()
@@ -441,13 +490,15 @@
         config_items = self.mcd.get_config_item_list(None, False)
         self.assertEqual(['Spec2'], config_items)
         config_items = self.mcd.get_config_item_list(None, True)
-        self.assertEqual(['Spec2/item1', 'Spec2/item2', 'Spec2/item3', 'Spec2/item4', 'Spec2/item5/', 'Spec2/item6/value1', 'Spec2/item6/value2'], config_items)
+        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", True)
-        self.assertEqual(['Spec2/item1', 'Spec2/item2', 'Spec2/item3', 'Spec2/item4', 'Spec2/item5/', 'Spec2/item6/value1', 'Spec2/item6/value2'], config_items)
+        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)
+        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)
+        self.assertEqual(['Spec2/item1', 'Spec2/item2', 'Spec2/item3', 'Spec2/item4', 'Spec2/item5', 'Spec2/item6/value1', 'Spec2/item6/value2'], config_items)
 
 if __name__ == '__main__':
     unittest.main()

Modified: branches/trac362/src/lib/python/isc/config/tests/module_spec_test.py
==============================================================================
--- branches/trac362/src/lib/python/isc/config/tests/module_spec_test.py (original)
+++ branches/trac362/src/lib/python/isc/config/tests/module_spec_test.py Mon Dec 27 18:21:26 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