Browse Source

sync with trunk

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac311@3018 e5f2f494-b856-4b98-b285-d166d9295462
JINMEI Tatuya 14 years ago
parent
commit
31d34a20f7
100 changed files with 2017 additions and 936 deletions
  1. 48 0
      ChangeLog
  2. 2 1
      README
  3. 26 6
      configure.ac
  4. 46 25
      doc/guide/bind10-guide.html
  5. 46 10
      doc/guide/bind10-guide.xml
  6. 2 2
      src/bin/auth/Makefile.am
  7. 1 1
      src/bin/auth/asio_link.cc
  8. 1 1
      src/bin/auth/asio_link.h
  9. 23 17
      src/bin/auth/auth_srv.cc
  10. 21 1
      src/bin/auth/auth_srv.h
  11. 1 0
      src/bin/auth/b10-auth.8
  12. 3 0
      src/bin/auth/b10-auth.xml
  13. 20 0
      src/bin/auth/benchmarks/Makefile.am
  14. 179 0
      src/bin/auth/benchmarks/query_bench.cc
  15. 6 6
      src/bin/auth/main.cc
  16. 30 17
      src/bin/auth/tests/auth_srv_unittest.cc
  17. 1 1
      src/bin/bind10/Makefile.am
  18. 2 0
      src/bin/bind10/bind10.8
  19. 12 5
      src/bin/bind10/bind10.py.in
  20. 6 0
      src/bin/bind10/bind10.xml
  21. 1 1
      src/bin/bind10/tests/Makefile.am
  22. 1 1
      src/bin/bindctl/Makefile.am
  23. 0 1
      src/bin/bindctl/bindcmd.py
  24. 1 1
      src/bin/bindctl/tests/Makefile.am
  25. 1 1
      src/bin/cfgmgr/Makefile.am
  26. 1 1
      src/bin/cfgmgr/tests/Makefile.am
  27. 1 1
      src/bin/cmdctl/Makefile.am
  28. 8 2
      src/bin/cmdctl/cmdctl.py.in
  29. 1 1
      src/bin/cmdctl/tests/Makefile.am
  30. 1 2
      src/bin/loadzone/Makefile.am
  31. 1 1
      src/bin/loadzone/tests/correct/Makefile.am
  32. 1 1
      src/bin/loadzone/tests/error/Makefile.am
  33. 1 1
      src/bin/msgq/Makefile.am
  34. 1 1
      src/bin/msgq/tests/Makefile.am
  35. 1 1
      src/bin/xfrin/Makefile.am
  36. 5 0
      src/bin/xfrin/TODO
  37. 45 10
      src/bin/xfrin/b10-xfrin.8
  38. 62 17
      src/bin/xfrin/b10-xfrin.xml
  39. 1 1
      src/bin/xfrin/tests/Makefile.am
  40. 3 3
      src/bin/xfrin/tests/xfrin_test.py
  41. 3 4
      src/bin/xfrin/xfrin.py.in
  42. 1 1
      src/bin/xfrout/Makefile.am
  43. 2 1
      src/bin/xfrout/TODO
  44. 18 9
      src/bin/xfrout/b10-xfrout.8
  45. 27 9
      src/bin/xfrout/b10-xfrout.xml
  46. 1 1
      src/bin/xfrout/tests/Makefile.am
  47. 53 7
      src/bin/xfrout/tests/xfrout_test.py
  48. 43 45
      src/bin/xfrout/xfrout.py.in
  49. 11 1
      src/bin/zonemgr/Makefile.am
  50. 5 0
      src/bin/zonemgr/TODO
  51. 91 0
      src/bin/zonemgr/b10-zonemgr.8
  52. 205 0
      src/bin/zonemgr/b10-zonemgr.xml
  53. 1 1
      src/bin/zonemgr/tests/Makefile.am
  54. 13 10
      src/bin/zonemgr/zonemgr.py.in
  55. 1 1
      src/lib/bench/tests/benchmark_unittest.cc
  56. 112 84
      src/lib/cc/data.cc
  57. 106 68
      src/lib/cc/data.h
  58. 18 16
      src/lib/cc/session.cc
  59. 18 18
      src/lib/cc/session.h
  60. 141 92
      src/lib/cc/tests/data_unittests.cc
  61. 2 2
      src/lib/cc/tests/session_unittests.cc
  62. 68 57
      src/lib/config/ccsession.cc
  63. 33 20
      src/lib/config/ccsession.h
  64. 28 26
      src/lib/config/config_data.cc
  65. 11 5
      src/lib/config/config_data.h
  66. 1 1
      src/lib/config/documentation.txt
  67. 51 42
      src/lib/config/module_spec.cc
  68. 22 13
      src/lib/config/module_spec.h
  69. 3 5
      src/lib/config/tests/Makefile.am
  70. 16 16
      src/lib/config/tests/ccsession_unittests.cc
  71. 25 46
      src/lib/config/tests/fake_session.cc
  72. 19 21
      src/lib/config/tests/fake_session.h
  73. 5 4
      src/lib/config/tests/module_spec_unittests.cc
  74. 6 1
      src/lib/datasrc/data_source.cc
  75. 2 2
      src/lib/datasrc/data_source.h
  76. 1 1
      src/lib/datasrc/sqlite3_datasrc.cc
  77. 1 1
      src/lib/datasrc/sqlite3_datasrc.h
  78. 1 1
      src/lib/datasrc/static_datasrc.cc
  79. 1 1
      src/lib/datasrc/static_datasrc.h
  80. 19 1
      src/lib/datasrc/tests/datasrc_unittest.cc
  81. 6 6
      src/lib/datasrc/tests/sqlite3_unittest.cc
  82. 1 2
      src/lib/datasrc/tests/test_datasrc.cc
  83. 1 1
      src/lib/datasrc/tests/test_datasrc.h
  84. 2 2
      src/lib/dns/Makefile.am
  85. 0 71
      src/lib/dns/message_test.py
  86. 1 1
      src/lib/dns/messagerenderer.h
  87. 7 8
      src/lib/dns/name.cc
  88. 10 21
      src/lib/dns/python/Makefile.am
  89. 6 6
      src/lib/dns/python/README
  90. 12 10
      src/lib/dns/python/message_python.cc
  91. 1 1
      src/lib/dns/python/messagerenderer_python.cc
  92. 14 14
      src/lib/dns/python/name_python.cc
  93. 141 0
      src/lib/dns/python/pydnspp.cc
  94. 1 1
      src/lib/dns/python/libdns_python_common.cc
  95. 0 0
      src/lib/dns/python/pydnspp_common.h
  96. 2 3
      src/lib/dns/python/question_python.cc
  97. 5 5
      src/lib/dns/python/rdata_python.cc
  98. 6 6
      src/lib/dns/python/rrclass_python.cc
  99. 3 3
      src/lib/dns/python/rrset_python.cc
  100. 0 0
      src/lib/dns/python/rrttl_python.cc

+ 48 - 0
ChangeLog

@@ -1,3 +1,51 @@
+  98.	[build]		jinmei
+	The ./configure script now tries to search some common include
+	paths for boost header files to minimize the need for explicit
+	configuration with --with-boost-include. (Trac #323, svn r3006)
+
+  97.	[func]		jinmei
+	Added a micro benchmark test for query processing of b10-auth.
+	(Trac #308, svn r2982)
+
+  96.	[bug]		jinmei
+	Fixed two small issues with configure: Do not set CXXFLAGS so that
+	it can be customized; Make sure --disable-static works.
+	(Trac #325, r2976)
+
+bind10-devel-20100917 released on September 17, 2010 
+
+  95.	[doc]		jreed
+	Add b10-zonemgr manual page. Update other docs to introduce
+	this secondary manager. (Trac #341, svn r2951)
+
+  95.	[bug]		jreed
+	bin/xfrout and bin/zonemgr: Fixed some stderr output.
+	(Trac #342, svn r2949)
+
+  94.	[bug]		jelte
+  	bin/xfrout:  Fixed a problem in xfrout where only 2 or 3 RRs
+	were used per DNS message in the xfrout stream.
+	(Trac #334, r2931)
+
+  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)
+
+  92.	[func]*		jelte
+	libdns_python (the python wrappers for libdns++) has been renamed
+	to pydnspp (Python DNS++). Programs and libraries that used
+	'import libdns_python' now need to use 'import pydnspp'.
+	(Trac #314, r2902)
+
+  91.	[func]*		jinmei
+	lib/cc: Use const pointers and const member functions for the API
+	as much as possible for safer operations.  Basically this does not
+	change the observable behavior, but some of the API were changed
+	in a backward incompatible manner.  This change also involves more
+	copies, but at this moment the overhead is deemed acceptable.
+	(Trac #310, r2803)
+
   90.	[build]		jinmei
 	(Darwin/Mac OS X specific) Specify DYLD_LIBRARY_PATH for tests and
 	experimental run under the source tree.  Without this loadable

+ 2 - 1
README

@@ -17,7 +17,8 @@ This release includes the bind10 master process, b10-msgq message
 bus, b10-auth authoritative DNS server (with SQLite3 backend),
 b10-cmdctl remote control daemon, b10-cfgmgr configuration manager,
 b10-xfrin AXFR inbound service, b10-xfrout outgoing AXFR service,
-and a new libdns++ library for C++ with a python wrapper.
+b10-zonemgr secondary manager, 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/

+ 26 - 6
configure.ac

@@ -40,6 +40,14 @@ AC_HELP_STRING([--enable-static-link],
   [enable_static_link=yes], [enable_static_link=no])
 AM_CONDITIONAL(USE_STATIC_LINK, test $enable_static_link = yes)
 
+# Check validity about some libtool options
+if test $enable_static_link = yes -a $enable_static = no; then
+	AC_MSG_ERROR([--enable-static-link requires --enable-static])
+fi
+if test $enable_shared = no; then
+	AC_MSG_ERROR([BIND 10 requires shared libraries to be built])
+fi
+
 # OS dependent configuration
 SET_ENV_LIBRARY_PATH=no
 ENV_LIBRARY_PATH=LD_LIBRARY_PATH
@@ -167,7 +175,6 @@ LDFLAGS=$LDFLAGS_SAVED
 # specify the default warning flags in CXXFLAGS and let specific modules
 # "override" the default.
 
-CXXFLAGS=-g
 werror_ok=0
 
 # SunStudio compiler requires special compiler options for boost
@@ -270,14 +277,31 @@ if test "$lcov" != "no"; then
 fi
 AC_SUBST(USE_LCOV)
 
+#
+# Configure Boost header path
+#
+# If explicitly specified, use it.
 AC_ARG_WITH([boost-include],
   AC_HELP_STRING([--with-boost-include=PATH],
     [specify exact directory for Boost headers]),
     [boost_include_path="$withval"])
+# If not specified, try some common paths.
+if test -z "$with_boost_include"; then
+	boostdirs="/usr/local /usr/pkg /opt /opt/local"
+	for d in $boostdirs
+	do
+		if test -f $d/include/boost/shared_ptr.hpp; then
+			boost_include_path=$d/include
+			break
+		fi
+	done
+fi
 if test "${boost_include_path}" ; then
 	BOOST_INCLUDES="-I${boost_include_path}"
 	CPPFLAGS="$CPPFLAGS $BOOST_INCLUDES"
 fi
+AC_CHECK_HEADERS([boost/shared_ptr.hpp boost/foreach.hpp],,
+  AC_MSG_ERROR([Missing required header files.]))
 AC_SUBST(BOOST_INCLUDES)
 
 #
@@ -389,10 +413,6 @@ if test "X$ac_cv_have_devpoll" = "Xyes" -a "X$GXX" = "Xyes"; then
 	CPPFLAGS="$CPPFLAGS -DASIO_DISABLE_DEV_POLL=1"
 fi
 
-# Check for headers from required devel kits.
-AC_CHECK_HEADERS([boost/shared_ptr.hpp boost/foreach.hpp],,
-  AC_MSG_ERROR([Missing required header files.]))
-
 AC_ARG_ENABLE(man, [AC_HELP_STRING([--enable-man],
   [regenerate man pages [default=no]])] ,enable_man=yes, enable_man=no)
 
@@ -423,6 +443,7 @@ AC_CONFIG_FILES([Makefile
                  src/bin/msgq/tests/Makefile
                  src/bin/auth/Makefile
                  src/bin/auth/tests/Makefile
+                 src/bin/auth/benchmarks/Makefile
                  src/bin/xfrin/Makefile
                  src/bin/xfrin/tests/Makefile
                  src/bin/xfrout/Makefile
@@ -544,7 +565,6 @@ Package:
 Flags:
   DEFS:          $DEFS
   CPPFLAGS:      $CPPFLAGS
-  CFLAGS:        $CFLAGS
   CXXFLAGS:      $CXXFLAGS
   B10_CXXFLAGS:  $B10_CXXFLAGS
 dnl includes too

File diff suppressed because it is too large
+ 46 - 25
doc/guide/bind10-guide.html


+ 46 - 10
doc/guide/bind10-guide.xml

@@ -42,7 +42,7 @@
 
     <note>
       <para>
-        BIND 10, at this time, does not provide an recursive
+        BIND 10, at this time, does not provide a recursive
         DNS server. It does provide a EDNS0- and DNSSEC-capable
         authoritative DNS server.
       </para>
@@ -74,9 +74,9 @@
 	For this development prototype release, the only supported
 	data source backend is SQLite3. The authoritative server
 	requires SQLite 3.3.9 or newer.
-        The <command>b10-xfrin</command> and <command>b10-xfrout</command>
-        modules require the libpython3 library and the Python
-        _sqlite3.so module.
+	The <command>b10-xfrin</command>, <command>b10-xfrout</command>,
+	and <command>b10-zonemgr</command> modules require the
+	libpython3 library and the Python _sqlite3.so module.
       </para></note>
 <!-- TODO: this will change ... -->
 
@@ -165,6 +165,15 @@
             </simpara>
           </listitem>
 
+          <listitem>
+            <simpara>
+              <command>b10-zonemgr</command> &mdash;
+              Secondary manager.
+	      This process keeps track of timers and other
+              necessary information for BIND 10 to act as a slave server.
+            </simpara>
+          </listitem>
+
         </itemizedlist>
       </para>
 
@@ -650,8 +659,9 @@ var/
       The <command>bind10</command> master process will also start up
       <command>b10-cmdctl</command> for admins to communicate with the
       system, <command>b10-auth</command> for Authoritative DNS service,
-      <command>b10-xfrin</command> for inbound DNS zone transfers.
-      and <command>b10-xfrout</command> for outbound DNS zone transfers.
+      <command>b10-xfrin</command> for inbound DNS zone transfers,
+      <command>b10-xfrout</command> for outbound DNS zone transfers,
+      and <command>b10-zonemgr</command> for secondary service.
     </para>
 
     <section id="start">
@@ -1173,14 +1183,14 @@ TODO
       transfer. When received, it is stored in the BIND 10
       data store, and its records can be served by
       <command>b10-auth</command>.
-      This allows the BIND 10 server to provide
-      <quote>secondary</quote> service.
+      In combination with <command>b10-zonemgr</command> (for
+      automated SOA checks), this allows the BIND 10 server to
+      provide <quote>secondary</quote> service.
     </para>
 
     <note><simpara>
      The current development release of BIND 10 only supports
      AXFR. (IXFR is not supported.) 
-     It also does not yet support automated SOA checks.
     </simpara></note>
 
     <para>
@@ -1204,12 +1214,13 @@ TODO
       sends the zone.
       This is used to provide master DNS service to share zones
       to secondary name servers.
+      The <command>b10-xfrout</command> is also used to send
+      NOTIFY messages to slaves.
     </para>
 
     <note><simpara>
      The current development release of BIND 10 only supports
      AXFR. (IXFR is not supported.) 
-     It also does not yet support NOTIFY.
      Access control is not yet provided.
     </simpara></note>
 
@@ -1226,6 +1237,31 @@ what is XfroutClient xfr_client??
 
   </chapter>
 
+  <chapter id="zonemgr">
+    <title>Secondary Manager</title>
+
+    <para>
+      The <command>b10-zonemgr</command> process is started by
+      <command>bind10</command>.
+      It keeps track of SOA refresh, retry, and expire timers
+      and other details for BIND 10 to perform as a slave.
+      When the <command>b10-auth</command> authoritative DNS server
+      receives a NOTIFY message, <command>b10-zonemgr</command>
+      may tell <command>b10-xfrin</command> to do a refresh
+      to start an inbound zone transfer.
+      The secondary manager resets its counters when a new zone is
+      transferred in.
+    </para>
+
+    <note><simpara>
+     Access control (such as allowing notifies) is not yet provided.
+     The primary/secondary service is not yet complete.
+    </simpara></note>
+
+<!-- TODO: lots to describe for zonemgr -->
+
+  </chapter>
+
 <!-- TODO: how to help: run unit tests, join lists, review trac tickets -->
 
   <!-- <index>    <title>Index</title> </index> -->

+ 2 - 2
src/bin/auth/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = . tests
+SUBDIRS = . tests benchmarks
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin
@@ -59,7 +59,7 @@ b10_auth_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
 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 += $(top_builddir)/src/bin/auth/libasio_link.a
+b10_auth_LDADD += libasio_link.a
 b10_auth_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
 b10_auth_LDADD += $(SQLITE_LIBS)
 

+ 1 - 1
src/bin/auth/asio_link.cc

@@ -67,7 +67,7 @@ IOAddress::toText() const {
 // Note: this implementation is optimized for the case where this object
 // is created from an ASIO endpoint object in a receiving code path
 // by avoiding to make a copy of the base endpoint.  For TCP it may not be
-// a bug deal, but when we receive UDP packets at a high rate, the copy
+// a big deal, but when we receive UDP packets at a high rate, the copy
 // overhead might be significant.
 class TCPEndpoint : public IOEndpoint {
 public:

+ 1 - 1
src/bin/auth/asio_link.h

@@ -207,7 +207,7 @@ public:
     /// will be thrown.
     ///
     /// Memory for the created object will be dynamically allocated.  It's
-    /// caller's responsibility to \c delete it later.
+    /// the caller's responsibility to \c delete it later.
     /// If resource allocation for the new object fails, a corresponding
     /// standard exception will be thrown.
     ///

+ 23 - 17
src/bin/auth/auth_srv.cc

@@ -69,7 +69,7 @@ private:
 public:
     AuthSrvImpl(const bool use_cache, AbstractXfroutClient& xfrout_client);
     ~AuthSrvImpl();
-    isc::data::ElementPtr setDbFile(const isc::data::ElementPtr config);
+    isc::data::ConstElementPtr setDbFile(isc::data::ConstElementPtr config);
 
     bool processNormalQuery(const IOMessage& io_message, Message& message,
                             MessageRenderer& response_renderer);
@@ -193,6 +193,16 @@ AuthSrv::getVerbose() const {
 }
 
 void
+AuthSrv::setCacheSlots(const size_t slots) {
+    impl_->cache_.setSlots(slots);
+}
+
+size_t
+AuthSrv::getCacheSlots() const {
+    return (impl_->cache_.getSlots());
+}
+
+void
 AuthSrv::setXfrinSession(AbstractSession* xfrin_session) {
     impl_->xfrin_session_ = xfrin_session;
 }
@@ -433,7 +443,7 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
     static const string command_template_end = "\"}]}";
 
     try {
-        ElementPtr notify_command = Element::fromJSON(
+        ConstElementPtr notify_command = Element::fromJSON(
                 command_template_start + question->getName().toText() + 
                 command_template_master + remote_ip_address +
                 command_template_rrclass + question->getClass().toText() +
@@ -441,7 +451,7 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
         const unsigned int seq =
             xfrin_session_->group_sendmsg(notify_command, "Zonemgr",
                                           "*", "*");
-        ElementPtr env, answer, parsed_answer;
+        ConstElementPtr env, answer, parsed_answer;
         xfrin_session_->group_recvmsg(env, answer, false, seq);
         int rcode;
         parsed_answer = parseAnswer(rcode, answer);
@@ -466,19 +476,17 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
     return (true);
 }
 
-ElementPtr
-AuthSrvImpl::setDbFile(const isc::data::ElementPtr config) {
-    ElementPtr answer = isc::config::createAnswer();
-    ElementPtr final;
+ConstElementPtr
+AuthSrvImpl::setDbFile(ConstElementPtr config) {
+    ConstElementPtr answer = isc::config::createAnswer();
 
     if (config && config->contains("database_file")) {
         db_file_ = config->get("database_file")->stringValue();
-        final = config;
     } else if (config_session_ != NULL) {
         bool is_default;
         string item("database_file");
-        ElementPtr value = config_session_->getValue(is_default, item);
-        final = Element::createMap();
+        ConstElementPtr value = config_session_->getValue(is_default, item);
+        ElementPtr final = Element::createMap();
 
         // If the value is the default, and we are running from
         // a specific directory ('from build'), we need to use
@@ -492,6 +500,7 @@ AuthSrvImpl::setDbFile(const isc::data::ElementPtr config) {
                                     "/bind10_zones.sqlite3");
         }
         final->set(item, value);
+        config = final;
 
         db_file_ = value->stringValue();
     } else {
@@ -508,7 +517,7 @@ AuthSrvImpl::setDbFile(const isc::data::ElementPtr config) {
     // fail, while acquiring resources in the RAII manner.  We then perform
     // delete and swap operations which should not fail.
     DataSrcPtr datasrc_ptr(DataSrcPtr(new Sqlite3DataSrc));
-    datasrc_ptr->init(final);
+    datasrc_ptr->init(config);
     data_sources_.addDataSrc(datasrc_ptr);
 
     // The following code should be exception free.
@@ -520,15 +529,12 @@ AuthSrvImpl::setDbFile(const isc::data::ElementPtr config) {
     return (answer);
 }
 
-ElementPtr
-AuthSrv::updateConfig(isc::data::ElementPtr new_config) {
+ConstElementPtr
+AuthSrv::updateConfig(ConstElementPtr new_config) {
     try {
         // the ModuleCCSession has already checked if we have
         // the correct ElementPtr type as specified in our .spec file
-        ElementPtr answer = isc::config::createAnswer();
-        answer = impl_->setDbFile(new_config);
-
-        return (answer);
+        return (impl_->setDbFile(new_config));
     } catch (const isc::Exception& error) {
         if (impl_->verbose_mode_) {
             cerr << "[b10-auth] error: " << error.what() << endl;

+ 21 - 1
src/bin/auth/auth_srv.h

@@ -69,10 +69,30 @@ public:
                         isc::dns::MessageRenderer& response_renderer);
     void setVerbose(bool on);
     bool getVerbose() const;
-    isc::data::ElementPtr updateConfig(isc::data::ElementPtr config);
+    isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr config);
     isc::config::ModuleCCSession* configSession() const;
     void setConfigSession(isc::config::ModuleCCSession* config_session);
 
+    /// \brief Set or update the size (number of slots) of hot spot cache.
+    ///
+    /// If the specified size is 0, it means the size will be unlimited.
+    /// The specified size is recorded even if the cache is disabled; the
+    /// new size will be effective when the cache is enabled.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \param slots The number of cache slots.
+    void setCacheSlots(const size_t slots);
+
+    /// \brief Get the current size (number of slots) of hot spot cache.
+    ///
+    /// It always returns the recorded size regardless of the cache is enabled.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return The current number of cache slots.
+    size_t getCacheSlots() const;
+
     ///
     /// Note: this interface is tentative.  We'll revisit the ASIO and session
     /// frameworks, at which point the session will probably be passed on

+ 1 - 0
src/bin/auth/b10-auth.8

@@ -137,6 +137,7 @@ configuration is not defined\&.
 \fBb10-cmdctl\fR(8),
 \fBb10-loadzone\fR(8),
 \fBb10-msgq\fR(8),
+\fBb10-zonemgr\fR(8),
 \fBbind10\fR(8),
 BIND 10 Guide\&.
 .SH "HISTORY"

+ 3 - 0
src/bin/auth/b10-auth.xml

@@ -201,6 +201,9 @@
         <refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
       <citerefentry>
+        <refentrytitle>b10-zonemgr</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
         <refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
       <citetitle>BIND 10 Guide</citetitle>.

+ 20 - 0
src/bin/auth/benchmarks/Makefile.am

@@ -0,0 +1,20 @@
+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_CXXFLAGS = $(B10_CXXFLAGS)
+
+CLEANFILES = *.gcno *.gcda
+
+noinst_PROGRAMS = query_bench
+query_bench_SOURCES = query_bench.cc
+query_bench_SOURCES += ../auth_srv.h ../auth_srv.cc
+
+query_bench_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
+query_bench_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+query_bench_LDADD += $(top_builddir)/src/lib/bench/libbench.la
+query_bench_LDADD += $(top_builddir)/src/lib/datasrc/libdatasrc.la
+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 += $(SQLITE_LIBS)

+ 179 - 0
src/bin/auth/benchmarks/query_bench.cc

@@ -0,0 +1,179 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <stdlib.h>
+
+#include <iostream>
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+
+#include <bench/benchmark.h>
+#include <bench/benchmark_util.h>
+
+#include <dns/buffer.h>
+#include <dns/message.h>
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/question.h>
+#include <dns/rrclass.h>
+
+#include <xfr/xfrout_client.h>
+
+#include <auth/auth_srv.h>
+#include <auth/asio_link.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dns;
+using namespace isc::xfr;
+using namespace isc::bench;
+using namespace asio_link;
+
+namespace {
+// Commonly used constant:
+XfroutClient xfrout_client("dummy_path"); // path doesn't matter
+
+class QueryBenchMark {
+private:
+    // Maintain dynamically generated objects via shared pointers because
+    // QueryBenchMark objects will be copied.
+    typedef boost::shared_ptr<AuthSrv> AuthSrvPtr;
+    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) :
+        server_(new AuthSrv(cache_slots >= 0 ? true : false, xfrout_client)),
+        queries_(queries),
+        query_message_(query_message),
+        renderer_(renderer),
+        dummy_socket(IOSocket::getDummyUDPSocket()),
+        dummy_endpoint(IOEndpointPtr(IOEndpoint::create(IPPROTO_UDP,
+                                                        IOAddress("192.0.2.1"),
+                                                        5300)))
+    {
+        if (cache_slots >= 0) {
+            server_->setCacheSlots(cache_slots);
+        }
+        server_->updateConfig(Element::fromJSON("{\"database_file\": \"" +
+                                                string(datasrc_file) + "\"}"));
+    }
+    unsigned int run() {
+        BenchQueries::const_iterator query;
+        const BenchQueries::const_iterator query_end = queries_.end();
+        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_);
+        }
+
+        return (queries_.size());
+    }
+private:
+    AuthSrvPtr server_;
+    const BenchQueries& queries_;
+    Message& query_message_;
+    MessageRenderer& renderer_;
+    IOSocket& dummy_socket;
+    IOEndpointPtr dummy_endpoint;
+};
+}
+
+namespace isc {
+namespace bench {
+template<>
+void
+BenchMark<QueryBenchMark>::printResult() const {
+    cout.precision(6);
+    cout << "Processed " << getIteration() << " queries in "
+         << fixed << getDuration() << "s";
+    cout.precision(2);
+    cout << " (" << fixed << getIterationPerSecond() << "qps)" << endl;
+}
+}
+}
+
+namespace {
+void
+usage() {
+    cerr << "Usage: query_bench [-n iterations] datasrc_file query_datafile"
+         << endl;
+    exit (1);
+}
+}
+
+int
+main(int argc, char* argv[]) {
+    int ch;
+    int iteration = 1;
+    while ((ch = getopt(argc, argv, "n:")) != -1) {
+        switch (ch) {
+        case 'n':
+            iteration = atoi(optarg);
+            break;
+        case '?':
+        default:
+            usage();
+        }
+    }
+    argc -= optind;
+    argv += optind;
+    if (argc < 2) {
+        usage();
+    }
+    const char* const datasrc_file = argv[0];
+    const char* const query_data_file = argv[1];
+
+    BenchQueries queries;
+    loadQueryData(query_data_file, queries, RRClass::IN());
+    OutputBuffer buffer(4096);
+    MessageRenderer renderer(buffer);
+    Message message(Message::PARSE);
+
+    cout << "Parameters:" << endl;
+    cout << "  Iterations: " << iteration << endl;
+    cout << "  Data Source: " << datasrc_file << endl;
+    cout << "  Query data: file=" << query_data_file << " (" << queries.size()
+         << " queries)" << endl << endl;
+
+    cout << "Benchmark enabling Hot Spot Cache with unlimited slots "
+         << endl;
+    BenchMark<QueryBenchMark>(iteration,
+                              QueryBenchMark(0, datasrc_file, queries, message,
+                                             renderer));
+
+    cout << "Benchmark enabling Hot Spot Cache with 10*#queries slots "
+         << endl;
+    BenchMark<QueryBenchMark>(iteration,
+                              QueryBenchMark(10 * queries.size(), datasrc_file,
+                                             queries, message, renderer));
+
+    cout << "Benchmark enabling Hot Spot Cache with #queries/2 slots "
+         << endl;
+    BenchMark<QueryBenchMark>(iteration,
+                              QueryBenchMark(queries.size() / 2, datasrc_file,
+                                             queries, message, renderer));
+
+    cout << "Benchmark disabling Hot Spot Cache" << endl;
+    BenchMark<QueryBenchMark>(iteration,
+                              QueryBenchMark(-1, datasrc_file, queries,
+                                             message, renderer));    
+
+    return (0);
+}

+ 6 - 6
src/bin/auth/main.cc

@@ -66,19 +66,19 @@ AuthSrv *auth_server;
 
 asio_link::IOService* io_service;
 
-ElementPtr
-my_config_handler(ElementPtr new_config) {
+ConstElementPtr
+my_config_handler(ConstElementPtr new_config) {
     return (auth_server->updateConfig(new_config));
 }
 
-ElementPtr
-my_command_handler(const string& command, const ElementPtr args) {
-    ElementPtr answer = createAnswer();
+ConstElementPtr
+my_command_handler(const string& command, ConstElementPtr args) {
+    ConstElementPtr answer = createAnswer();
 
     if (command == "print_message") {
         cout << args << endl;
         /* let's add that message to our answer as well */
-        answer->get("result")->add(args);
+        answer = createAnswer(0, args);
     } else if (command == "shutdown") {
         io_service->stop();
     }

+ 30 - 17
src/bin/auth/tests/auth_srv_unittest.cc

@@ -85,26 +85,27 @@ private:
         {}
         virtual void establish(const char* socket_file);
         virtual void disconnect();
-        virtual int group_sendmsg(ElementPtr msg, string group,
+        virtual int group_sendmsg(ConstElementPtr msg, string group,
                                   string instance, string to);
-        virtual bool group_recvmsg(ElementPtr& envelope, ElementPtr& msg,
+        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(ElementPtr& envelope, ElementPtr& newmsg);
-        virtual bool hasQueuedMsgs();
+        virtual int reply(ConstElementPtr envelope, ConstElementPtr newmsg);
+        virtual bool hasQueuedMsgs() const;
         virtual void setTimeout(size_t timeout UNUSED_PARAM) {};
         virtual size_t getTimeout() const { return 0; };
 
-        void setMessage(ElementPtr msg) { msg_ = msg; }
+        void setMessage(ConstElementPtr msg) { msg_ = msg; }
         void disableSend() { send_ok_ = false; }
         void disableReceive() { receive_ok_ = false; }
 
-        ElementPtr sent_msg;
+        ConstElementPtr sent_msg;
         string msg_destination;
     private:
-        ElementPtr msg_;
+        ConstElementPtr msg_;
         bool send_ok_;
         bool receive_ok_;
     };
@@ -174,19 +175,19 @@ AuthSrvTest::MockSession::startRead(
 {}
 
 int
-AuthSrvTest::MockSession::reply(ElementPtr& envelope UNUSED_PARAM,
-                                ElementPtr& newmsg UNUSED_PARAM)
+AuthSrvTest::MockSession::reply(ConstElementPtr envelope UNUSED_PARAM,
+                                ConstElementPtr newmsg UNUSED_PARAM)
 {
     return (-1);
 }
 
 bool
-AuthSrvTest::MockSession::hasQueuedMsgs() {
+AuthSrvTest::MockSession::hasQueuedMsgs() const {
     return (false);
 }
 
 int
-AuthSrvTest::MockSession::group_sendmsg(ElementPtr msg, string group,
+AuthSrvTest::MockSession::group_sendmsg(ConstElementPtr msg, string group,
                                         string instance UNUSED_PARAM,
                                         string to UNUSED_PARAM)
 {
@@ -200,8 +201,8 @@ AuthSrvTest::MockSession::group_sendmsg(ElementPtr msg, string group,
 }
 
 bool
-AuthSrvTest::MockSession::group_recvmsg(ElementPtr& envelope UNUSED_PARAM,
-                                        ElementPtr& msg,
+AuthSrvTest::MockSession::group_recvmsg(ConstElementPtr& envelope UNUSED_PARAM,
+                                        ConstElementPtr& msg,
                                         bool nonblock UNUSED_PARAM,
                                         int seq UNUSED_PARAM)
 {
@@ -546,7 +547,8 @@ TEST_F(AuthSrvTest, notify) {
     EXPECT_EQ("Zonemgr", notify_session.msg_destination);
     EXPECT_EQ("notify",
               notify_session.sent_msg->get("command")->get(0)->stringValue());
-    ElementPtr notify_args = notify_session.sent_msg->get("command")->get(1);
+    ConstElementPtr notify_args =
+        notify_session.sent_msg->get("command")->get(1);
     EXPECT_EQ("example.com.", notify_args->get("zone_name")->stringValue());
     EXPECT_EQ(DEFAULT_REMOTE_ADDRESS,
               notify_args->get("master")->stringValue());
@@ -574,7 +576,8 @@ TEST_F(AuthSrvTest, notifyForCHClass) {
 
     // Other conditions should be the same, so simply confirm the RR class is
     // set correctly.
-    ElementPtr notify_args = notify_session.sent_msg->get("command")->get(1);
+    ConstElementPtr notify_args =
+        notify_session.sent_msg->get("command")->get(1);
     EXPECT_EQ("CH", notify_args->get("zone_class")->stringValue());
 }
 
@@ -702,12 +705,12 @@ void
 updateConfig(AuthSrv* server, const char* const dbfile,
              const bool expect_success)
 {
-    const ElementPtr config_answer =
+    ConstElementPtr config_answer =
         server->updateConfig(Element::fromJSON(dbfile));
     EXPECT_EQ(Element::map, config_answer->getType());
     EXPECT_TRUE(config_answer->contains("result"));
 
-    const ElementPtr result = config_answer->get("result");
+    ConstElementPtr result = config_answer->get("result");
     EXPECT_EQ(Element::list, result->getType());
     EXPECT_EQ(expect_success ? 0 : 1, result->get(0)->intValue());
 }
@@ -754,4 +757,14 @@ TEST_F(AuthSrvTest, updateConfigFail) {
     headerCheck(parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
                 QR_FLAG | AA_FLAG, 1, 1, 1, 0);
 }
+
+TEST_F(AuthSrvTest, cacheSlots) {
+    // simple check for the get/set operations
+    server.setCacheSlots(10);    // 10 = arbitrary choice
+    EXPECT_EQ(10, server.getCacheSlots());
+
+    // 0 is a valid size
+    server.setCacheSlots(0);
+    EXPECT_EQ(00, server.getCacheSlots());
+}
 }

+ 1 - 1
src/bin/bind10/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 sbin_SCRIPTS = bind10
 CLEANFILES = bind10 bind10.pyc

+ 2 - 0
src/bin/bind10/bind10.8

@@ -101,6 +101,8 @@ and its child processes\&.
 \fBb10-cmdctl\fR(8),
 \fBb10-msgq\fR(8),
 \fBb10-xfrin\fR(8),
+\fBb10-xfrout\fR(8),
+\fBb10-zonemgr\fR(8),
 BIND 10 Guide\&.
 .SH "HISTORY"
 .PP

+ 12 - 5
src/bin/bind10/bind10.py.in

@@ -18,7 +18,7 @@
 """\
 This file implements the Boss of Bind (BoB, or bob) program.
 
-It's purpose is to start up the BIND 10 system, and then manage the
+Its purpose is to start up the BIND 10 system, and then manage the
 processes, by starting and stopping processes, plus restarting
 processes that exit.
 
@@ -65,7 +65,9 @@ import posix
 import isc.cc
 
 # This is the version that gets displayed to the user.
-__version__ = "v20100531"
+# 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@)"
 
 class RestartSchedule:
     """
@@ -627,7 +629,7 @@ def main():
 
 
     # Parse any command-line options.
-    parser = OptionParser(version=__version__)
+    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)")
@@ -680,7 +682,7 @@ def main():
 
     # Announce startup.
     if options.verbose:
-        sys.stdout.write("BIND 10 %s\n" % __version__)
+        sys.stdout.write("%s\n" % VERSION)
 
     # TODO: set process name, perhaps by:
     #       http://code.google.com/p/procname/
@@ -735,7 +737,12 @@ def main():
 
         for fd in rlist + xlist:
             if fd == ccs_fd:
-                boss_of_bind.ccs.check_command()
+                try:
+                    boss_of_bind.ccs.check_command()
+                except isc.cc.session.ProtocolError:
+                    if options.verbose:
+                        sys.stderr.write("[bind10] msgq channel disappeared.\n")
+                    break
             elif fd == wakeup_fd:
                 os.read(wakeup_fd, 32)
 

+ 6 - 0
src/bin/bind10/bind10.xml

@@ -189,6 +189,12 @@
       <citerefentry>
         <refentrytitle>b10-xfrin</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
+      <citerefentry>
+        <refentrytitle>b10-xfrout</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>b10-zonemgr</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
       <citetitle>BIND 10 Guide</citetitle>.
     </para>
   </refsect1>

+ 1 - 1
src/bin/bind10/tests/Makefile.am

@@ -8,5 +8,5 @@ check-local:
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/bind10 \
-	$(PYCOVERAGE) $(abs_srcdir)/$$pytest ; \
+	$(PYCOVERAGE) $(abs_srcdir)/$$pytest || exit ; \
 	done

+ 1 - 1
src/bin/bindctl/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 bin_SCRIPTS = bindctl
 man_MANS = bindctl.1

+ 0 - 1
src/bin/bindctl/bindcmd.py

@@ -35,7 +35,6 @@ import os, time, random, re
 import getpass
 from hashlib import sha1
 import csv
-import json
 import pwd
 import getpass
 

+ 1 - 1
src/bin/bindctl/tests/Makefile.am

@@ -8,5 +8,5 @@ check-local:
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_srcdir)/src/bin \
-	$(PYCOVERAGE) $(abs_srcdir)/$$pytest ; \
+	$(PYCOVERAGE) $(abs_srcdir)/$$pytest || exit ; \
 	done

+ 1 - 1
src/bin/cfgmgr/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 

+ 1 - 1
src/bin/cfgmgr/tests/Makefile.am

@@ -9,5 +9,5 @@ check-local:
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/cfgmgr \
-	$(PYCOVERAGE) $(abs_builddir)/$$pytest ; \
+	$(PYCOVERAGE) $(abs_builddir)/$$pytest || exit ; \
 	done

+ 1 - 1
src/bin/cmdctl/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 

+ 8 - 2
src/bin/cmdctl/cmdctl.py.in

@@ -380,6 +380,7 @@ class CommandControl():
     def send_command(self, module_name, command_name, params = None):
         '''Send the command from bindctl to proper module. '''
         errstr = 'unknown error'
+        answer = None
         if self._verbose:
             self.log_info("Begin send command '%s' to module '%s'" %(command_name, module_name))
 
@@ -390,7 +391,10 @@ class CommandControl():
             msg = ccsession.create_command(command_name, params)
             seq = self._cc.group_sendmsg(msg, module_name)
             #TODO, it may be blocked, msqg need to add a new interface waiting in timeout.
-            answer, env = self._cc.group_recvmsg(False, seq)
+            try:
+                answer, env = self._cc.group_recvmsg(False, seq)
+            except isc.cc.session.SessionTimeout:
+                errstr = "Module '%s' not responding" % module_name
 
         if self._verbose:
             self.log_info("Finish send command '%s' to module '%s'" % (command_name, module_name))
@@ -410,7 +414,6 @@ class CommandControl():
             except ccsession.ModuleCCSessionError as mcse:
                 errstr = str("Error in ccsession answer:") + str(mcse)
                 self.log_info(errstr)
-        
         return 1, {'error': errstr}
     
     def log_info(self, msg):
@@ -602,6 +605,9 @@ if __name__ == '__main__':
     except isc.cc.SessionError as err:
         sys.stderr.write("[b10-cmdctl] Error creating b10-cmdctl, "
                          "is the command channel daemon running?\n")        
+    except isc.cc.SessionTimeout:
+        sys.stderr.write("[b10-cmdctl] Error creating b10-cmdctl, "
+                         "is the configuration manager running?\n")        
     except KeyboardInterrupt:
         sys.stderr.write("[b10-cmdctl] exit from Cmdctl\n")
     except CmdctlException as err:

+ 1 - 1
src/bin/cmdctl/tests/Makefile.am

@@ -10,5 +10,5 @@ check-local:
 	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/cmdctl \
 	CMDCTL_SPEC_PATH=$(abs_top_builddir)/src/bin/cmdctl \
 	CMDCTL_SRC_PATH=$(abs_top_srcdir)/src/bin/cmdctl \
-	$(PYCOVERAGE) $(abs_srcdir)/$$pytest ; \
+	$(PYCOVERAGE) $(abs_srcdir)/$$pytest || exit ; \
 	done

+ 1 - 2
src/bin/loadzone/Makefile.am

@@ -1,5 +1,4 @@
-SUBDIRS = tests/correct
-SUBDIRS += tests/error
+SUBDIRS = . tests/correct tests/error
 bin_SCRIPTS = b10-loadzone
 
 CLEANFILES = b10-loadzone

+ 1 - 1
src/bin/loadzone/tests/correct/Makefile.am

@@ -21,5 +21,5 @@ check-local:
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/loadzone \
-	$(SHELL) $(abs_builddir)/$$pytest ; \
+	$(SHELL) $(abs_builddir)/$$pytest || exit ; \
 	done

+ 1 - 1
src/bin/loadzone/tests/error/Makefile.am

@@ -21,5 +21,5 @@ check-local:
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/bin/loadzone \
-	$(SHELL) $(abs_builddir)/$$pytest ; \
+	$(SHELL) $(abs_builddir)/$$pytest || exit ; \
 	done

+ 1 - 1
src/bin/msgq/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
  

+ 1 - 1
src/bin/msgq/tests/Makefile.am

@@ -8,6 +8,6 @@ check-local:
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	env PYTHONPATH=$(abs_top_builddir)/src/bin/msgq:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \
-	$(PYCOVERAGE) $(abs_srcdir)/$$pytest ; \
+	$(PYCOVERAGE) $(abs_srcdir)/$$pytest || exit ; \
 	done
 

+ 1 - 1
src/bin/xfrin/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 

+ 5 - 0
src/bin/xfrin/TODO

@@ -63,3 +63,8 @@
     which a shutdown notification would be sent to the child.  With
     this approach each thread needs to watch at least two channels,
     and then it would need some asynchronous communication mechanism.
+17. Do zone transfer from notifyfrom address first, if it's one master of the zone.
+18. Check soa serial first when doing zone refreshment.
+19. Add configuration items to seperate zone, including ACL, multiple masters, etc.
+20. Be able to cancel the ongoing zone transfer, and be able to disable zone transfer.
+

+ 45 - 10
src/bin/xfrin/b10-xfrin.8

@@ -2,12 +2,12 @@
 .\"     Title: b10-xfrin
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: March 17, 2010
+.\"      Date: September 8, 2010
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-XFRIN" "8" "March 17, 2010" "BIND10" "BIND10"
+.TH "B10\-XFRIN" "8" "September 8, 2010" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -47,7 +47,6 @@ The Y1 prototype release only supports AXFR\&. IXFR is not implemented\&.
 .sp .5v
 .RE
 .PP
-
 This daemon communicates with BIND 10 over a
 \fBb10-msgq\fR(8)
 C\-Channel connection\&. If this connection is not established,
@@ -60,24 +59,59 @@ receives its configurations from
 \fBb10-cfgmgr\fR(8)\&.
 .SH "CONFIGURATION AND COMMANDS"
 .PP
-The configurable setting is
+The configurable settings are:
+.PP
+\fImaster_addr\fR
+The default is 127\&.0\&.0\&.1\&.
+.PP
+\fImaster_port\fR
+The default is 53\&.
+.PP
 \fItransfers\-in\fR
-which defines the maximum number of inbound zone transfers that can run concurrently\&. The default is 10\&.
+defines the maximum number of inbound zone transfers that can run concurrently\&. The default is 10\&.
 .PP
 The configuration commands are:
 .PP
 
-\fBshutdown\fR
-stops all incoming zone transfers and exits
-\fBb10\-xfrin\fR\&. (Note that the BIND 10 boss process will restart this service\&.)
+\fBnotify\fR
+is sent by
+\fBb10-zonemgr\fR(8)
+when a DNS NOTIFY message is received to initiate a zone transfer\&.
+This is an internal command and not exposed to the administrator\&.
+.PP
+
+\fBrefresh\fR
+triggers the transfer in for a single zone\&. It is the same as
+\fBretransfer\fR
+except it checks the SOA serial first\&.
+This is an internal command and not exposed to the administrator\&.
+
+.PP
+
+\fBrefresh_from_zonemgr\fR
+is sent by
+\fBb10-zonemgr\fR(8)
+according to the SOA\'s REFRESH time to tell
+\fBb10\-xfrin\fR
+that the zone needs to do a zone refresh\&. This is an internal command and not exposed to the administrator\&.
 .PP
 
 \fBretransfer\fR
 triggers the transfer in for a single zone without checking the zone\'s serial number\&. It has the following arguments:
 \fIzone_name\fR
-to define the zone to request and
+to define the zone to request,
+\fIzone_class\fR
+to define the class (defaults to
+\(lqIN\(rq),
 \fImaster\fR
-to define the IP address of the authoritative server to transfer from\&.
+to define the IP address of the authoritative server to transfer from, and
+\fIport\fR
+to define the port number on the authoritative server (defaults to 53)\&.
+.PP
+
+\fBshutdown\fR
+stops all incoming zone transfers and exits
+\fBb10\-xfrin\fR\&. (Note that the BIND 10 boss process will restart this service\&.)
 .if n \{\
 .sp
 .\}
@@ -99,6 +133,7 @@ This prototype version uses SQLite3 as its data source backend\&. Future version
 
 \fBb10-cfgmgr\fR(8),
 \fBb10-msgq\fR(8),
+\fBb10-zonemgr\fR(8),
 \fBbind10\fR(8),
 BIND 10 Guide\&.
 .SH "HISTORY"

+ 62 - 17
src/bin/xfrin/b10-xfrin.xml

@@ -21,7 +21,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>March 17, 2010</date>
+    <date>September 8, 2010</date>
   </refentryinfo>
 
   <refmeta>
@@ -68,7 +68,6 @@
     </simpara></note>
 
     <para>
-<!-- TODO: does it really use msgq? what for? -->
       This daemon communicates with BIND 10 over a
       <citerefentry><refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum></citerefentry>
       C-Channel connection.  If this connection is not established,
@@ -85,42 +84,85 @@
   <refsect1>
     <title>CONFIGURATION AND COMMANDS</title>
     <para>
-      The configurable setting is <varname>transfers-in</varname>
-      which defines the maximum number of inbound zone transfers
+      The configurable settings are:
+    </para>
+
+    <para><varname>master_addr</varname>
+<!-- TODO: how can there be a single setting for this? -->
+       The default is 127.0.0.1.
+    </para>
+
+    <para><varname>master_port</varname>
+<!-- TODO: what if custom is needed per zone? -->
+      The default is 53.
+    </para>
+
+    <para><varname>transfers-in</varname>
+      defines the maximum number of inbound zone transfers
       that can run concurrently. The default is 10.
     </para>
 
 <!-- TODO: formating -->
-<!-- TODO: refresh is code but not in spec -->
-<!-- schedule immediate maintenance for a zone(check soa serial ) -->
     <para>
       The configuration commands are:
     </para>
+
+    <para>
+      <command>notify</command> is sent by
+      <citerefentry><refentrytitle>b10-zonemgr</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      when a DNS NOTIFY message is received to initiate a zone
+      transfer.
+<!-- TODO: document that zonemgr or xfrin checks if it needs to or not -->
+      This is an internal command and not exposed to the administrator.
+<!-- not defined in spec -->
+    </para>
+
     <para>
-      <command>shutdown</command> stops all incoming zone transfers
-      and exits <command>b10-xfrin</command>. (Note that the BIND 10
-      boss process will restart this service.)
+      <command>refresh</command> triggers the transfer in for
+      a single zone.
+      It is the same as <command>retransfer</command> except it
+      checks the SOA serial first.
+<!-- TODO more detail -->
+      This is an internal command and not exposed to the administrator.
+<!-- not defined in spec -->
+<!-- TODO: refresh is code but not in spec, see trac ticket #328 -->
     </para>
+
+    <para>
+      <command>refresh_from_zonemgr</command> is sent by
+      <citerefentry><refentrytitle>b10-zonemgr</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      according to the SOA's REFRESH time
+      to tell <command>b10-xfrin</command> that the zone needs to do
+      a zone refresh.
+      This is an internal command and not exposed to the administrator. 
+<!-- not defined in spec -->
+    </para>
+
     <para>
       <command>retransfer</command> triggers the transfer in for
       a single zone without checking the zone's serial number.
       It has the following arguments: <varname>zone_name</varname>
-      to define the zone to request and <varname>master</varname>
-      to define the IP address of the authoritative server to
-      transfer from.
+      to define the zone to request,
+      <varname>zone_class</varname> to define the class (defaults to
+      <quote>IN</quote>),
+      <varname>master</varname> to define the IP address of
+      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? -->
 
+    <para>
+      <command>shutdown</command> stops all incoming zone transfers
+      and exits <command>b10-xfrin</command>. (Note that the BIND 10
+      boss process will restart this service.)
+    </para>
 <!-- TODO:
 add a usage example of xfrin -->
 
 <!-- TODO:
 
-port (defaults to 53)
-db_file (defaults to zone.sqlite3) --> <!-- TODO: fix this -->
-
-<!-- TODO:
-
  later it will can be triggered by :
 1.  Notify message from auth server.
 2.  Schedule zone transfer (determined by Refresh/Expire time in SOA record)
@@ -182,6 +224,9 @@ operation
         <refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
       <citerefentry>
+        <refentrytitle>b10-zonemgr</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
         <refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
       <citetitle>BIND 10 Guide</citetitle>.

+ 1 - 1
src/bin/xfrin/tests/Makefile.am

@@ -16,5 +16,5 @@ check-local:
 	echo Running test: $$pytest ; \
 	env PYTHONPATH=$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/bin/xfrin:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \
 	$(LIBRARY_PATH_PLACEHOLDER) \
-	$(PYCOVERAGE) $(abs_srcdir)/$$pytest ; \
+	$(PYCOVERAGE) $(abs_srcdir)/$$pytest || exit ; \
 	done

+ 3 - 3
src/bin/xfrin/tests/xfrin_test.py

@@ -508,12 +508,12 @@ class TestXfrin(unittest.TestCase):
                                                   self.args)['result'][0], 1)
 
     def test_command_handler_retransfer_nomodule(self):
-        dns_module = sys.modules['libdns_python'] # this must exist
-        del sys.modules['libdns_python']
+        dns_module = sys.modules['pydnspp'] # this must exist
+        del sys.modules['pydnspp']
         self.assertEqual(self.xfr.command_handler("retransfer",
                                                   self.args)['result'][0], 1)
         # sys.modules is global, so we must recover it
-        sys.modules['libdns_python'] = dns_module
+        sys.modules['pydnspp'] = dns_module
 
     def test_command_handler_refresh(self):
         # at this level, refresh is no different than retransfer.

+ 3 - 4
src/bin/xfrin/xfrin.py.in

@@ -30,7 +30,7 @@ from optparse import OptionParser, OptionValueError
 from isc.config.ccsession import *
 from isc.notify import notify_out
 try:
-    from libdns_python import *
+    from pydnspp import *
 except ImportError as e:
     # C++ loadable module may not be installed; even so the xfrin process
     # must keep running, so we warn about it and move forward.
@@ -197,7 +197,6 @@ class XfrinConnection(asyncore.dispatcher):
                                             self._handle_xfrin_response)
 
                 self.log_msg(logstr + 'succeeded')
-                ret = XFRIN_OK
 
         except XfrinException as e:
             self.log_msg(e)
@@ -523,8 +522,8 @@ class Xfrin:
 
     def xfrin_start(self, zone_name, rrclass, db_file, master_addrinfo,
                     check_soa = True):
-        if "libdns_python" not in sys.modules:
-            return (1, "xfrin failed, can't load dns message python library: 'libdns_python'")
+        if "pydnspp" not in sys.modules:
+            return (1, "xfrin failed, can't load dns message python library: 'pydnspp'")
 
         # check max_transfer_in, else return quota error
         if self.recorder.count() >= self._max_transfers_in:

+ 1 - 1
src/bin/xfrout/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 

+ 2 - 1
src/bin/xfrout/TODO

@@ -1 +1,2 @@
-Add unittest code.
+Add unittest code.
+Be able to cancel the outgoing zone transfer, and also be able to disable outgoing zone transfer.

+ 18 - 9
src/bin/xfrout/b10-xfrout.8

@@ -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: April 20, 2010
+.\"      Date: September 8, 2010
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-XFROUT" "8" "April 20, 2010" "BIND10" "BIND10"
+.TH "B10\-XFROUT" "8" "September 8, 2010" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -27,7 +27,7 @@ b10-xfrout \- Outbound DNS zone transfer service
 .PP
 The
 \fBb10\-xfrout\fR
-daemon provides the BIND 10 outgoing DNS zone transfer service\&. Normally it is started by the
+daemon provides the BIND 10 outgoing DNS zone transfer service\&. It is also used to send outgoing NOTIFY messages\&. Normally it is started by the
 \fBbind10\fR(8)
 boss process\&. When the
 \fBb10\-auth\fR
@@ -67,13 +67,13 @@ receives its configurations from
 The configurable settings are:
 .PP
 
-\fItransfers\-out\fR
-defines the maximum number of outgoing zone transfers that can run concurrently\&. The default is 10\&.
-.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 \{\
 .sp
 .\}
@@ -91,25 +91,34 @@ This prototype version uses SQLite3 as its data source backend\&. Future version
 .sp .5v
 .RE
 .PP
-The configuration command is:
+The configuration commands are:
 .PP
 
 \fBshutdown\fR
 stops all outbound zone transfers and exits
 \fBb10\-xfrout\fR\&. (Note that the BIND 10 boss process will restart this service\&.)
+.PP
+
+\fBzone_new_data_ready\fR
+is sent from
+\fBb10-xfrin\fR(8)
+to indicate that the zone transferred in successfully\&. This triggers
+\fBb10\-xfrout\fR
+to send NOTIFY message(s)\&. This is an internal command and not exposed to the administrator\&.
 .SH "SEE ALSO"
 .PP
 
 \fBb10-auth\fR(8),
 \fBb10-cfgmgr\fR(8),
 \fBb10-msgq\fR(8),
+\fBb10-xfrin\fR(8),
 \fBbind10\fR(8),
 BIND 10 Guide\&.
 .SH "HISTORY"
 .PP
 The
 \fBb10\-xfrout\fR
-daemon was implemented in March 2010 by Zhang Likun of CNNIC for the ISC BIND 10 project\&.
+daemon was first implemented in March 2010 by Zhang Likun of CNNIC for the ISC BIND 10 project\&.
 .SH "COPYRIGHT"
 .br
 Copyright \(co 2010 Internet Systems Consortium, Inc. ("ISC")

+ 27 - 9
src/bin/xfrout/b10-xfrout.xml

@@ -21,7 +21,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>April 20, 2010</date>
+    <date>September 8, 2010</date>
   </refentryinfo>
 
   <refmeta>
@@ -54,6 +54,7 @@
     <title>DESCRIPTION</title>
     <para>The <command>b10-xfrout</command> daemon provides the BIND 10
       outgoing DNS zone transfer service.
+      It is also used to send outgoing NOTIFY messages.
       Normally it is started by the
       <citerefentry><refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum></citerefentry>
       boss process.
@@ -93,17 +94,21 @@
       The configurable settings are:
     </para>
     <para>
-      <varname>transfers-out</varname>
-      defines the maximum number of outgoing zone transfers
-      that can run concurrently. The default is 10.
-    </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>
+      defines the maximum number of outgoing zone transfers
+      that can run concurrently. The default is 10.
     </para>
 
+<!-- TODO: log configurations not documented yet in here. jreed
+     has some but waiting on decisions ... -->
+
     <note><simpara>
       This prototype version uses SQLite3 as its data source backend.
       Future versions will be configurable, supporting multiple
@@ -112,7 +117,7 @@
 
 <!-- TODO: formating -->
     <para>
-      The configuration command is:
+      The configuration commands are:
     </para>
     <para>
       <command>shutdown</command> stops all outbound zone transfers
@@ -120,6 +125,16 @@
       boss process will restart this service.)
     </para>
 
+    <para>
+      <command>zone_new_data_ready</command> is sent from
+      <citerefentry><refentrytitle>b10-xfrin</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      to indicate that the zone transferred in successfully.
+      This triggers <command>b10-xfrout</command> to send NOTIFY
+      message(s).
+      This is an internal command and not exposed to the administrator.
+<!-- not defined in spec -->
+    </para>
+
   </refsect1>
 
 <!--
@@ -161,6 +176,9 @@
         <refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
       <citerefentry>
+        <refentrytitle>b10-xfrin</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
         <refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
       <citetitle>BIND 10 Guide</citetitle>.
@@ -170,8 +188,8 @@
   <refsect1>
     <title>HISTORY</title>
     <para>
-      The <command>b10-xfrout</command> daemon was implemented in March 2010
-      by Zhang Likun of CNNIC for the ISC BIND 10 project.
+      The <command>b10-xfrout</command> daemon was first implemented
+      in March 2010 by Zhang Likun of CNNIC for the ISC BIND 10 project.
     </para>
   </refsect1>
 </refentry><!--

+ 1 - 1
src/bin/xfrout/tests/Makefile.am

@@ -16,5 +16,5 @@ check-local:
 	echo Running test: $$pytest ; \
 	env PYTHONPATH=$(abs_top_builddir)/src/bin/xfrout:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/xfr/.libs \
 	$(LIBRARY_PATH_PLACEHOLDER) \
-	$(PYCOVERAGE) $(abs_srcdir)/$$pytest ; \
+	$(PYCOVERAGE) $(abs_srcdir)/$$pytest || exit ; \
 	done

+ 53 - 7
src/bin/xfrout/tests/xfrout_test.py

@@ -19,7 +19,7 @@
 import unittest
 import os
 from isc.cc.session import *
-from libdns_python import *
+from pydnspp import *
 from xfrout import *
 
 # our fake socket, where we can read and insert messages
@@ -40,8 +40,12 @@ class MySocket():
         return len(data)
 
     def readsent(self):
-        result = self.sendqueue[:]
-        del self.sendqueue[:]
+        if len(self.sendqueue) >= 2:
+            size = 2 + struct.unpack("!H", self.sendqueue[:2])[0]
+        else:
+            size = 0
+        result = self.sendqueue[:size]
+        self.sendqueue = self.sendqueue[size:]
         return result
     
     def read_msg(self):
@@ -133,7 +137,7 @@ class TestXfroutSession(unittest.TestCase):
 
         msg = self.getmsg()
         msg.make_response()
-        self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa)
+        self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa, 0)
         get_msg = self.sock.read_msg()
 
         self.assertEqual(get_msg.get_rr_count(Section.QUESTION()), 1)
@@ -148,10 +152,52 @@ class TestXfroutSession(unittest.TestCase):
         rdata = answer.get_rdata()
         self.assertEqual(rdata[0].to_text(), self.soa_record[7])
 
-    def test_get_message_len(self):
+    def test_trigger_send_message_with_last_soa(self):
+        rrset_a = RRset(Name("example.com"), RRClass.IN(), RRType.A(), RRTTL(3600))
+        rrset_a.add_rdata(Rdata(RRType.A(), RRClass.IN(), "192.0.2.1"))
+        rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
+
         msg = self.getmsg()
-        msg.make_response()  
-        self.assertEqual(self.xfrsess._get_message_len(msg), 29)
+        msg.make_response()
+
+        msg.add_rrset(Section.ANSWER(), rrset_a)
+        # give the function a value that is larger than MAX-len(rrset)
+        self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa, 65520)
+
+        # this should have triggered the sending of two messages
+        # (1 with the rrset we added manually, and 1 that triggered
+        # the sending in _with_last_soa)
+        get_msg = self.sock.read_msg()
+        self.assertEqual(get_msg.get_rr_count(Section.QUESTION()), 1)
+        self.assertEqual(get_msg.get_rr_count(Section.ANSWER()), 1)
+        self.assertEqual(get_msg.get_rr_count(Section.AUTHORITY()), 0)
+
+        answer = get_msg.get_section(Section.ANSWER())[0]
+        self.assertEqual(answer.get_name().to_text(), "example.com.")
+        self.assertEqual(answer.get_class(), RRClass("IN"))
+        self.assertEqual(answer.get_type().to_text(), "A")
+        rdata = answer.get_rdata()
+        self.assertEqual(rdata[0].to_text(), "192.0.2.1")
+
+        get_msg = self.sock.read_msg()
+        self.assertEqual(get_msg.get_rr_count(Section.QUESTION()), 0)
+        self.assertEqual(get_msg.get_rr_count(Section.ANSWER()), 1)
+        self.assertEqual(get_msg.get_rr_count(Section.AUTHORITY()), 0)
+
+        #answer_rrset_iter = section_iter(get_msg, section.ANSWER())
+        answer = get_msg.get_section(Section.ANSWER())[0]
+        self.assertEqual(answer.get_name().to_text(), "example.com.")
+        self.assertEqual(answer.get_class(), RRClass("IN"))
+        self.assertEqual(answer.get_type().to_text(), "SOA")
+        rdata = answer.get_rdata()
+        self.assertEqual(rdata[0].to_text(), self.soa_record[7])
+
+        # and it should not have sent anything else
+        self.assertEqual(0, len(self.sock.sendqueue))
+
+    def test_get_rrset_len(self):
+        rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
+        self.assertEqual(82, get_rrset_len(rrset_soa))
 
     def test_zone_is_empty(self):
         global sqlite3_ds

+ 43 - 45
src/bin/xfrout/xfrout.py.in

@@ -27,7 +27,7 @@ from socketserver import *
 import os
 from isc.config.ccsession import *
 from isc.log.log import *
-from isc.cc import SessionError
+from isc.cc import SessionError, SessionTimeout
 from isc.notify import notify_out
 import socket
 import select
@@ -35,7 +35,7 @@ import errno
 from optparse import OptionParser, OptionValueError
 try:
     from libxfr_python import *
-    from libdns_python import *
+    from pydnspp import *
 except ImportError as e:
     # C++ loadable module may not be installed; even so the xfrout process
     # must keep running, so we warn about it and move forward.
@@ -57,6 +57,15 @@ AUTH_SPECFILE_LOCATION = AUTH_SPECFILE_PATH + os.sep + "auth.spec"
 MAX_TRANSFERS_OUT = 10
 VERBOSE_MODE = False
 
+XFROUT_MAX_MESSAGE_SIZE = 65535
+
+def get_rrset_len(rrset):
+    """Returns the wire length of the given RRset"""
+    bytes = bytearray()
+    rrset.to_wire(bytes)
+    return len(bytes)
+
+
 class XfroutSession(BaseRequestHandler):
     def __init__(self, request, client_address, server, log):
         # The initializer for the superclass may call functions
@@ -121,10 +130,8 @@ class XfroutSession(BaseRequestHandler):
 
 
     def _send_message(self, sock, msg):
-        #obuf = output_buffer(0)
-        #render = message_render(obuf)
         render = MessageRenderer()
-        render.set_length_limit(65535)
+        render.set_length_limit(XFROUT_MAX_MESSAGE_SIZE)
         msg.to_wire(render)
         header_len = struct.pack('H', socket.htons(render.get_length()))
         self._send_data(sock, header_len)
@@ -227,34 +234,20 @@ class XfroutSession(BaseRequestHandler):
         rrset_.add_rdata(rdata_)
         return rrset_
          
-    def _send_message_with_last_soa(self, msg, sock, rrset_soa):
+    def _send_message_with_last_soa(self, msg, sock, rrset_soa, message_upper_len):
         '''Add the SOA record to the end of message. If it can't be
         added, a new message should be created to send out the last soa .
         '''
+        rrset_len = get_rrset_len(rrset_soa)
 
-        render = MessageRenderer()
-        msg.to_wire(render)
-        old_message_len = render.get_length()
-        msg.add_rrset(Section.ANSWER(), rrset_soa)
-
-        msg.to_wire(render)
-        message_len = render.get_length()
-
-        if message_len != old_message_len:
-            self._send_message(sock, msg)
+        if message_upper_len + rrset_len < XFROUT_MAX_MESSAGE_SIZE:
+            msg.add_rrset(Section.ANSWER(), rrset_soa)
         else:
+            self._send_message(sock, msg)
             msg = self._clear_message(msg)
             msg.add_rrset(Section.ANSWER(), rrset_soa)
-            self._send_message(sock, msg)
-
-    def _get_message_len(self, msg):
-        '''Get message length, every time need do like this? Actually there should be 
-        a better way, I need check with jinmei later.
-        '''
 
-        render = MessageRenderer()
-        msg.to_wire(render)
-        return render.get_length()
+        self._send_message(sock, msg)
 
 
     def _reply_xfrout_query(self, msg, sock, zone_name):
@@ -265,9 +258,8 @@ class XfroutSession(BaseRequestHandler):
         rrset_soa = self._create_rrset_from_db_record(soa_record)
         msg.add_rrset(Section.ANSWER(), rrset_soa)
 
-        old_message_len = 0
-        # TODO, Since add_rrset() return nothing when rrset can't be added, so I have to compare
-        # the message length to know if the rrset has been added sucessfully.
+        message_upper_len = get_rrset_len(rrset_soa)
+
         for rr_data in sqlite3_ds.get_zone_datas(zone_name, self.server.get_db_file()):
             if  self.server._shutdown_event.is_set(): # Check if xfrout is shutdown
                 self._log.log_message("error", "shutdown!")
@@ -277,19 +269,22 @@ class XfroutSession(BaseRequestHandler):
                 continue
 
             rrset_ = self._create_rrset_from_db_record(rr_data)
-            msg.add_rrset(Section.ANSWER(), rrset_)
-            message_len = self._get_message_len(msg)
-            if message_len != old_message_len:
-                old_message_len = message_len
+
+            # We calculate the maximum size of the RRset (i.e. the
+            # size without compression) and use that to see if we
+            # may have reached the limit
+            rrset_len = get_rrset_len(rrset_)
+            if message_upper_len + rrset_len < XFROUT_MAX_MESSAGE_SIZE:
+                msg.add_rrset(Section.ANSWER(), rrset_)
+                message_upper_len += rrset_len
                 continue
 
             self._send_message(sock, msg)
             msg = self._clear_message(msg)
             msg.add_rrset(Section.ANSWER(), rrset_) # Add the rrset to the new message
-            old_message_len = 0
-
-        self._send_message_with_last_soa(msg, sock, rrset_soa)
+            message_upper_len = rrset_len
 
+        self._send_message_with_last_soa(msg, sock, rrset_soa, message_upper_len)
 
 class UnixSockServer(ThreadingUnixStreamServer):
     '''The unix domain socket server which accept xfr query sent from auth server.'''
@@ -315,8 +310,8 @@ class UnixSockServer(ThreadingUnixStreamServer):
         If it's not a socket file or nobody is listening
         , it will be removed. If it can't be removed, exit from python. '''
         if self._sock_file_in_use(sock_file):
-            print("[b10-xfrout] Fail to start xfrout process, unix socket" 
-                  " file '%s' is being used by another xfrout process" % sock_file)
+            sys.stderr.write("[b10-xfrout] Fail to start xfrout process, unix socket" 
+                  " file '%s' is being used by another xfrout process\n" % sock_file)
             sys.exit(0)
         else:
             if not os.path.exists(sock_file):
@@ -325,7 +320,7 @@ class UnixSockServer(ThreadingUnixStreamServer):
             try:
                 os.unlink(sock_file)
             except OSError as err:
-                print('[b10-xfrout] Fail to remove file ' + sock_file, err)
+                sys.stderr.write('[b10-xfrout] Fail to remove file %s: %s\n' % (sock_file, err))
                 sys.exit(0)
    
     def _sock_file_in_use(self, sock_file):
@@ -409,9 +404,9 @@ class XfroutServer:
         self._listen_sock_file = UNIX_SOCKET_FILE 
         self._shutdown_event = threading.Event()
         self._cc = isc.config.ModuleCCSession(SPECFILE_LOCATION, self.config_handler, self.command_handler)
-        self._cc.add_remote_config(AUTH_SPECFILE_LOCATION);
         self._config_data = self._cc.get_full_config()
         self._cc.start()
+        self._cc.add_remote_config(AUTH_SPECFILE_LOCATION);
         self._log = isc.log.NSLogger(self._config_data.get('log_name'), self._config_data.get('log_file'),
                                 self._config_data.get('log_severity'), self._config_data.get('log_versions'),
                                 self._config_data.get('log_max_bytes'), True)
@@ -481,8 +476,8 @@ class XfroutServer:
             zone_name = args.get('zone_name')
             zone_class = args.get('zone_class')
             if zone_name and zone_class:
-                self._log.log_message("info", "Receive notify command for zone:'%s/%s'" \
-                                     % (zone_name, zone_class))
+                self._log.log_message("info", "zone '%s/%s': receive notify others command" \
+                                       % (zone_name, zone_class))
                 self.send_notify(zone_name, zone_class)
                 answer = create_answer(0)
             else:
@@ -525,12 +520,15 @@ if '__main__' == __name__:
         xfrout_server = XfroutServer()
         xfrout_server.run()
     except KeyboardInterrupt:
-        sys.stderr.write("[b10-xfrout] exit xfrout process")
+        sys.stderr.write("[b10-xfrout] exit xfrout process\n")
     except SessionError as e:
-        sys.stderr.write("[b10-xfrout] Error creating xfrout," 
-                           "is the command channel daemon running?")
+        sys.stderr.write("[b10-xfrout] Error creating xfrout, "
+                           "is the command channel daemon running?\n")
+    except SessionTimeout as e:
+        sys.stderr.write("[b10-xfrout] Error creating xfrout, " 
+                           "is the configuration manager running?\n")
     except ModuleCCSessionError as e:
-        sys.stderr.write("info", '[b10-xfrout] exit xfrout process:', e)
+        sys.stderr.write("[b10-xfrout] exit xfrout process:%s\n" % str(e))
 
     if xfrout_server:
         xfrout_server.shutdown()

+ 11 - 1
src/bin/zonemgr/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 
@@ -9,6 +9,16 @@ b10_zonemgr_DATA = zonemgr.spec
 
 CLEANFILES = b10-zonemgr zonemgr.pyc zonemgr.spec
 
+man_MANS = b10-zonemgr.8
+EXTRA_DIST = $(man_MANS) b10-zonemgr.xml
+
+if ENABLE_MAN
+
+b10-zonemgr.8: b10-zonemgr.xml
+	xsltproc --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-zonemgr.xml
+
+endif
+
 zonemgr.spec: zonemgr.spec.pre
 	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" zonemgr.spec.pre >$@
 

+ 5 - 0
src/bin/zonemgr/TODO

@@ -1 +1,6 @@
 1. Zonemgr should support adding/deleting zones dynamically.
+2. Make zonemgr  has customizable configurations for LOWERBOUND_REFRESH, LOWERBOUND_RETRY, MAX_TRANSFER_TIMEOUT, REFRESH_OFFSET, RETRY_OFFSET, EXPIRED_OFFSET, and/or jitter?
+3. There should be one way to see the current counters/timers and other data for each zone managed by zonemgr.
+4. There should be one way to turn off zonemgr.(Does user really need it? not sure what's the purpose of user)
+
+

+ 91 - 0
src/bin/zonemgr/b10-zonemgr.8

@@ -0,0 +1,91 @@
+'\" t
+.\"     Title: b10-zonemgr
+.\"    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
+.\"    Manual: BIND10
+.\"    Source: BIND10
+.\"  Language: English
+.\"
+.TH "B10\-ZONEMGR" "8" "September 8, 2010" "BIND10" "BIND10"
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+b10-zonemgr \- BIND 10 Secondary zone manager
+.SH "SYNOPSIS"
+.HP \w'\fBb10\-zonemgr\fR\ 'u
+\fBb10\-zonemgr\fR [\fB\-v\fR] [\fB\-\-verbose\fR]
+.SH "DESCRIPTION"
+.PP
+The
+\fBb10\-zonemgr\fR
+daemon, also known as the BIND 10 secondary manager, keeps track of timers and other information necessary for BIND 10 to act as a DNS slave\&. Normally it is started by the
+\fBbind10\fR(8)
+boss process\&.
+.PP
+This daemon communicates with BIND 10 over a
+\fBb10-msgq\fR(8)
+C\-Channel connection\&. If this connection is not established,
+\fBb10\-zonemgr\fR
+will exit\&.
+.PP
+
+\fBb10\-zonemgr\fR
+receives its configurations from
+\fBb10-cfgmgr\fR(8)\&.
+.SH "CONFIGURATION AND COMMANDS"
+.PP
+The configuration commands are:
+.PP
+
+\fBnotify\fR
+(sent by
+\fBb10-auth\fR(8)) tells
+\fBb10\-zonemgr\fR
+the zone name and class, and the IP address for the master (source of the NOTIFY message)\&. This will set the zone\'s refresh time to now\&.
+This is an internal command and not exposed to the administrator\&.
+.PP
+
+\fBshutdown\fR
+exits
+\fBb10\-zonemgr\fR\&. (Note that the BIND 10 boss process will restart this service\&.)
+.PP
+
+\fBzone_new_data_ready\fR
+is sent from
+\fBb10-xfrin\fR(8)
+to indicate that the zone transferred in successfully\&. This is an internal command and not exposed to the administrator\&.
+.PP
+
+\fBzone_xfrin_failed\fR
+is sent from
+\fBb10-xfrin\fR(8)
+to indicate a failure (such as a transfer\-in was incomplete)\&. The refresh timer for the zone is reset\&.
+This is an internal command and not exposed to the administrator\&.
+.SH "SEE ALSO"
+.PP
+
+\fBb10-auth\fR(8),
+\fBb10-cfgmgr\fR(8),
+\fBb10-msgq\fR(8),
+\fBb10-xfrin\fR(8),
+\fBb10-xfrout\fR(8),
+\fBbind10\fR(8),
+BIND 10 Guide\&.
+.SH "HISTORY"
+.PP
+The
+\fBb10\-zonemgr\fR
+daemon was designed in July 2010 by CNNIC for the ISC BIND 10 project\&.
+.SH "COPYRIGHT"
+.br
+Copyright \(co 2010 Internet Systems Consortium, Inc. ("ISC")
+.br

+ 205 - 0
src/bin/zonemgr/b10-zonemgr.xml

@@ -0,0 +1,205 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+               "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
+	       [<!ENTITY mdash "&#8212;">]>
+<!--
+ - Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+ -
+ - Permission to use, copy, modify, and/or distribute this software for any
+ - purpose with or without fee is hereby granted, provided that the above
+ - copyright notice and this permission notice appear in all copies.
+ -
+ - THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ - AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ - OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ - PERFORMANCE OF THIS SOFTWARE.
+-->
+
+<!-- $Id$ -->
+<refentry>
+
+  <refentryinfo>
+    <date>September 8, 2010</date>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>b10-zonemgr</refentrytitle>
+    <manvolnum>8</manvolnum>
+    <refmiscinfo>BIND10</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>b10-zonemgr</refname>
+    <refpurpose>BIND 10 Secondary zone manager</refpurpose>
+  </refnamediv>
+
+  <docinfo>
+    <copyright>
+      <year>2010</year>
+      <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
+    </copyright>
+  </docinfo>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>b10-zonemgr</command>
+      <arg><option>-v</option></arg>
+      <arg><option>--verbose</option></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>DESCRIPTION</title>
+    <para>The <command>b10-zonemgr</command> daemon, also known
+      as the BIND 10 secondary manager, keeps track of timers
+      and other information necessary for BIND 10 to act as a DNS slave.
+      Normally it is started by the
+      <citerefentry><refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      boss process.
+    </para>
+
+    <para>
+      This daemon communicates with BIND 10 over a
+      <citerefentry><refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      C-Channel connection.  If this connection is not established,
+      <command>b10-zonemgr</command> will exit.
+<!-- TODO what if connection closes later, will b10-zonemgr exit? -->
+    </para>
+
+    <para>
+     <command>b10-zonemgr</command> receives its configurations from
+<citerefentry><refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+    </para>
+
+<!--
+
+            self._send_command(XFRIN_MODULE_NAME, ZONE_NOTIFY_COMMAND, param) 
+            self._clear_zone_notifier_master(zone_name_class)
+        # Send refresh command to xfrin module
+        else:
+            param = {"zone_name" : zone_name_class[0],
+                     "zone_class" : zone_name_class[1]
+                    }
+            self._send_command(XFRIN_MODULE_NAME, ZONE_REFRESH_COMMAND, param)
+
+-->
+
+  </refsect1>
+
+  <refsect1>
+    <title>CONFIGURATION AND COMMANDS</title>
+<!--
+    <para>
+      The configurable settings are:
+    </para>
+-->
+
+<!-- TODO: formating -->
+    <para>
+      The configuration commands are:
+    </para>
+    <para>
+      <command>notify</command> (sent by
+      <citerefentry><refentrytitle>b10-auth</refentrytitle><manvolnum>8</manvolnum></citerefentry>)
+      tells <command>b10-zonemgr</command>
+      the zone name and class, and the IP address for the master
+      (source of the NOTIFY message).
+      This will set the zone's refresh time to now.
+<!-- TODO reword this -->
+      This is an internal command and not exposed to the administrator.
+<!-- not defined in spec -->
+    </para>
+
+    <para>
+      <command>shutdown</command> exits <command>b10-zonemgr</command>.
+      (Note that the BIND 10 boss process will restart this service.)
+    </para>
+
+    <para>
+      <command>zone_new_data_ready</command> is sent from
+      <citerefentry><refentrytitle>b10-xfrin</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      to indicate that the zone transferred in successfully.
+      This is an internal command and not exposed to the administrator.
+<!-- not defined in spec -->
+    </para>
+
+    <para>
+      <command>zone_xfrin_failed</command> is sent from
+      <citerefentry><refentrytitle>b10-xfrin</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      to indicate a failure (such as a transfer-in was incomplete).
+      The refresh timer for the zone is reset.
+<!--
+        """Set zone next refresh time after zone refresh fail.
+           now + retry*3/4 <= next_refresh_time <= now + retry
+-->
+      This is an internal command and not exposed to the administrator.
+<!-- not defined in spec -->
+    </para>
+
+  </refsect1>
+
+<!--
+  <refsect1>
+    <title>OPTIONS</title>
+
+    <para>The arguments are as follows:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><option></option></term>
+        <listitem><para>
+        </para></listitem>
+      </varlistentry>
+
+    </variablelist>
+
+  </refsect1>
+-->
+<!--
+  <refsect1>
+    <title>FILES</title>
+    <para>
+    <filename>/tmp/auth_xfrout_conn</filename>
+    </para>
+  </refsect1>
+-->
+
+  <refsect1>
+    <title>SEE ALSO</title>
+    <para>
+      <citerefentry>
+        <refentrytitle>b10-auth</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>b10-xfrin</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>b10-xfrout</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citetitle>BIND 10 Guide</citetitle>.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>HISTORY</title>
+    <para>
+      The <command>b10-zonemgr</command> daemon was designed in July 2010
+      by CNNIC for the ISC BIND 10 project.
+    </para>
+  </refsect1>
+</refentry><!--
+ - Local variables:
+ - mode: sgml
+ - End:
+-->

+ 1 - 1
src/bin/zonemgr/tests/Makefile.am

@@ -8,5 +8,5 @@ check-local:
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	env PYTHONPATH=$(abs_top_builddir)/src/bin/zonemgr:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/xfr/.libs \
-	$(PYCOVERAGE) $(abs_srcdir)/$$pytest ; \
+	$(PYCOVERAGE) $(abs_srcdir)/$$pytest || exit ; \
 	done

+ 13 - 10
src/bin/zonemgr/zonemgr.py.in

@@ -153,8 +153,8 @@ class ZonemgrRefresh:
     def zone_refresh_success(self, zone_name_class):
         """Update zone info after zone refresh success"""
         if (self._zone_not_exist(zone_name_class)):
-            raise ZonemgrException("[b10-zonemgr] Zone (%s, %s) doesn't \
-                                    belong to zonemgr" % zone_name_class)
+            raise ZonemgrException("[b10-zonemgr] Zone (%s, %s) doesn't "
+                                   "belong to zonemgr" % zone_name_class)
             return
         self.zonemgr_reload_zone(zone_name_class)
         self._set_zone_refresh_timer(zone_name_class)
@@ -164,8 +164,8 @@ class ZonemgrRefresh:
     def zone_refresh_fail(self, zone_name_class):
         """Update zone info after zone refresh fail"""
         if (self._zone_not_exist(zone_name_class)):
-            raise ZonemgrException("[b10-zonemgr] Zone (%s, %s) doesn't \
-                                    belong to zonemgr" % zone_name_class)
+            raise ZonemgrException("[b10-zonemgr] Zone (%s, %s) doesn't "
+                                   "belong to zonemgr" % zone_name_class)
             return
         self._set_zone_state(zone_name_class, ZONE_OK)
         self._set_zone_retry_timer(zone_name_class)
@@ -173,8 +173,8 @@ class ZonemgrRefresh:
     def zone_handle_notify(self, zone_name_class, master):
         """Handle zone notify"""
         if (self._zone_not_exist(zone_name_class)):
-            raise ZonemgrException("[b10-zonemgr] Notified zone (%s, %s) doesn't \
-                                    belong to zonemgr" % zone_name_class)
+            raise ZonemgrException("[b10-zonemgr] Notified zone (%s, %s) "
+                                   "doesn't belong to zonemgr" % zone_name_class)
             return
         self._set_zone_notifier_master(zone_name_class, master)
         self._set_zone_notify_timer(zone_name_class)
@@ -513,12 +513,15 @@ if '__main__' == __name__:
         zonemgrd = Zonemgr()
         zonemgrd.run()
     except KeyboardInterrupt:
-        sys.stderr.write("[b10-zonemgr] exit zonemgr process")
+        sys.stderr.write("[b10-zonemgr] exit zonemgr process\n")
     except isc.cc.session.SessionError as e:
-        sys.stderr.write("[b10-zonemgr] Error creating ,zonemgr" 
-                           "is the command channel daemon running?")
+        sys.stderr.write("[b10-zonemgr] Error creating zonemgr, " 
+                           "is the command channel daemon running?\n")
+    except isc.cc.session.SessionTimeout as e:
+        sys.stderr.write("[b10-zonemgr] Error creating zonemgr, " 
+                           "is the configuration manager running?\n")
     except isc.config.ModuleCCSessionError as e:
-        sys.stderr.write("info", "[b10-zonemgr] exit zonemgr process:", e)
+        sys.stderr.write("[b10-zonemgr] exit zonemgr process: %s\n" % str(e))
 
     if zonemgrd:
         zonemgrd.shutdown()

+ 1 - 1
src/lib/bench/tests/benchmark_unittest.cc

@@ -58,7 +58,7 @@ BenchMark<TestBenchMark>::tearDown() {
 };
 
 // XXX: some compilers cannot find class static constants used in
-// EXPECT_xxx macross, for which we need an explicit definition.
+// EXPECT_xxx macros, for which we need an explicit definition.
 template <typename T>
 const int BenchMark<T>::TIME_FAILURE;
 }

+ 112 - 84
src/lib/cc/data.cc

@@ -20,6 +20,7 @@
 
 #include <cassert>
 #include <climits>
+#include <map>
 #include <cstdio>
 #include <iostream>
 #include <string>
@@ -35,21 +36,21 @@ namespace isc {
 namespace data {
 
 std::string
-Element::str() {
+Element::str() const {
     std::stringstream ss;
     toJSON(ss);
     return (ss.str());
 }
 
 std::string
-Element::toWire() {
+Element::toWire() const {
     std::stringstream ss;
     toJSON(ss);
     return (ss.str());
 }
 
 void
-Element::toWire(std::ostream& ss) {
+Element::toWire(std::ostream& ss) const {
     toJSON(ss);
 }
 
@@ -81,12 +82,12 @@ Element::getValue(std::string& t UNUSED_PARAM) {
 }
 
 bool
-Element::getValue(std::vector<ElementPtr>& t UNUSED_PARAM) {
+Element::getValue(std::vector<ConstElementPtr>& t UNUSED_PARAM) {
     return (false);
 }
 
 bool
-Element::getValue(std::map<std::string, ElementPtr>& t UNUSED_PARAM) {
+Element::getValue(std::map<std::string, ConstElementPtr>& t UNUSED_PARAM) {
     return (false);
 }
 
@@ -111,27 +112,29 @@ Element::setValue(const std::string& v UNUSED_PARAM) {
 }
 
 bool
-Element::setValue(const std::vector<ElementPtr>& v UNUSED_PARAM) {
+Element::setValue(const std::vector<ConstElementPtr>& v UNUSED_PARAM) {
     return (false);
 }
 
 bool
-Element::setValue(const std::map<std::string, ElementPtr>& v UNUSED_PARAM) {
+Element::setValue(const std::map<std::string,
+                  ConstElementPtr>& v UNUSED_PARAM)
+{
     return (false);
 }
 
-ElementPtr
-Element::get(const int i UNUSED_PARAM) {
+ConstElementPtr
+Element::get(const int i UNUSED_PARAM) const {
     isc_throw(TypeError, "get(int) called on a non-list Element");
 }
 
 void
-Element::set(const size_t i UNUSED_PARAM, ElementPtr element UNUSED_PARAM) {
+Element::set(const size_t i UNUSED_PARAM, ConstElementPtr element UNUSED_PARAM) {
     isc_throw(TypeError, "set(int, element) called on a non-list Element");
 }
 
 void
-Element::add(ElementPtr element UNUSED_PARAM) {
+Element::add(ConstElementPtr element UNUSED_PARAM) {
     isc_throw(TypeError, "add() called on a non-list Element");
 }
 
@@ -141,18 +144,18 @@ Element::remove(const int i UNUSED_PARAM) {
 }
 
 size_t
-Element::size() {
+Element::size() const {
     isc_throw(TypeError, "size() called on a non-list Element");
 }
 
-ElementPtr
-Element::get(const std::string& name UNUSED_PARAM) {
+ConstElementPtr
+Element::get(const std::string& name UNUSED_PARAM) const {
     isc_throw(TypeError, "get(string) called on a non-map Element");
 }
 
 void
 Element::set(const std::string& name UNUSED_PARAM,
-             ElementPtr element UNUSED_PARAM)
+             ConstElementPtr element UNUSED_PARAM)
 {
     isc_throw(TypeError, "set(name, element) called on a non-map Element");
 }
@@ -163,18 +166,18 @@ Element::remove(const std::string& name UNUSED_PARAM) {
 }
 
 bool
-Element::contains(const std::string& name UNUSED_PARAM) {
+Element::contains(const std::string& name UNUSED_PARAM) const {
     isc_throw(TypeError, "contains(string) called on a non-map Element");
 }
 
-ElementPtr
-Element::find(const std::string& identifier UNUSED_PARAM) {
+ConstElementPtr
+Element::find(const std::string& identifier UNUSED_PARAM) const {
     isc_throw(TypeError, "find(string) called on a non-map Element");
 }
 
 bool
 Element::find(const std::string& identifier UNUSED_PARAM,
-              ElementPtr& t UNUSED_PARAM)
+              ConstElementPtr t UNUSED_PARAM) const
 {
     return (false);
 }
@@ -189,12 +192,18 @@ throwJSONError(const std::string& error, const std::string& file, int line, int
 }
 }
 
-std::ostream& operator <<(std::ostream &out, const isc::data::ElementPtr& e) {
-    return (out << e->str());
+std::ostream&
+operator<<(std::ostream &out, const Element& e) {
+    return (out << e.str());
 }
 
-bool operator==(const isc::data::ElementPtr a, const isc::data::ElementPtr b) {
-    return (a->equals(b));
+bool
+operator==(const Element& a, const Element& b) {
+    return (a.equals(b));
+}
+
+bool operator!=(const Element& a, const Element& b) {
+    return (!a.equals(b));
 };
 
 //
@@ -428,7 +437,7 @@ from_stringstream_list(std::istream &in, const std::string& file, int& line, int
 {
     char c = 0;
     ElementPtr list = Element::createList();
-    ElementPtr cur_list_element;
+    ConstElementPtr cur_list_element;
 
     skip_chars(in, " \t\n", line, pos);
     while (c != EOF && c != ']') {
@@ -462,7 +471,7 @@ from_stringstream_map(std::istream &in, const std::string& file, int& line,
             in.get();
             pos++;
 
-            ElementPtr value = Element::fromJSON(in, file, line, pos);
+            ConstElementPtr value = Element::fromJSON(in, file, line, pos);
             map->set(key, value);
             
             skip_to(in, file, line, pos, ",}", " \t\n");
@@ -614,20 +623,17 @@ Element::fromJSON(const std::string &in) {
 // to JSON format
 
 void
-IntElement::toJSON(std::ostream& ss)
-{
+IntElement::toJSON(std::ostream& ss) const {
     ss << intValue();
 }
 
 void
-DoubleElement::toJSON(std::ostream& ss)
-{
+DoubleElement::toJSON(std::ostream& ss) const {
     ss << doubleValue();
 }
 
 void
-BoolElement::toJSON(std::ostream& ss)
-{
+BoolElement::toJSON(std::ostream& ss) const {
     if (boolValue()) {
         ss << "true";
     } else {
@@ -636,26 +642,23 @@ BoolElement::toJSON(std::ostream& ss)
 }
 
 void
-NullElement::toJSON(std::ostream& ss)
-{
+NullElement::toJSON(std::ostream& ss) const {
     ss << "null";
 }
 
 void
-StringElement::toJSON(std::ostream& ss)
-{
+StringElement::toJSON(std::ostream& ss) const {
     ss << "\"";
     ss << stringValue();
     ss << "\"";
 }
 
 void
-ListElement::toJSON(std::ostream& ss)
-{
+ListElement::toJSON(std::ostream& ss) const {
     ss << "[ ";
 
-    const std::vector<ElementPtr>& v = listValue();
-    for (std::vector<ElementPtr>::const_iterator it = v.begin();
+    const std::vector<ConstElementPtr>& v = listValue();
+    for (std::vector<ConstElementPtr>::const_iterator it = v.begin();
          it != v.end(); ++it) {
         if (it != v.begin()) {
             ss << ", ";
@@ -666,12 +669,11 @@ ListElement::toJSON(std::ostream& ss)
 }
 
 void
-MapElement::toJSON(std::ostream& ss)
-{
+MapElement::toJSON(std::ostream& ss) const {
     ss << "{ ";
 
-    const std::map<std::string, ElementPtr>& m = mapValue();
-    for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
+    const std::map<std::string, ConstElementPtr>& m = mapValue();
+    for (std::map<std::string, ConstElementPtr>::const_iterator it = m.begin();
          it != m.end(); ++it) {
         if (it != m.begin()) {
             ss << ", ";
@@ -690,13 +692,13 @@ MapElement::toJSON(std::ostream& ss)
 // we're looking for) is not a MapElement
 // returns 0 if it could simply not be found
 // should that also be an exception?
-ElementPtr
-MapElement::find(const std::string& id) {
+ConstElementPtr
+MapElement::find(const std::string& id) const {
     const size_t sep = id.find('/');
     if (sep == std::string::npos) {
         return (get(id));
     } else {
-        ElementPtr ce = get(id.substr(0, sep));
+        ConstElementPtr ce = get(id.substr(0, sep));
         if (ce) {
             // ignore trailing slash
             if  (sep + 1 != id.size()) {
@@ -735,14 +737,14 @@ Element::fromWire(std::stringstream& in, int length) {
 }
 
 void
-MapElement::set(const std::string& key, ElementPtr value) {
+MapElement::set(const std::string& key, ConstElementPtr value) {
     m[key] = value;
 }
 
 bool
-MapElement::find(const std::string& id, ElementPtr& t) {
+MapElement::find(const std::string& id, ConstElementPtr t) const {
     try {
-        ElementPtr p = find(id);
+        ConstElementPtr p = find(id);
         if (p) {
             t = p;
             return (true);
@@ -754,43 +756,43 @@ MapElement::find(const std::string& id, ElementPtr& t) {
 }
 
 bool
-IntElement::equals(ElementPtr other) {
-    return (other->getType() == Element::integer) &&
-           (i == other->intValue());
+IntElement::equals(const Element& other) const {
+    return (other.getType() == Element::integer) &&
+           (i == other.intValue());
 }
 
 bool
-DoubleElement::equals(ElementPtr other) {
-    return (other->getType() == Element::real) &&
-           (d == other->doubleValue());
+DoubleElement::equals(const Element& other) const {
+    return (other.getType() == Element::real) &&
+           (d == other.doubleValue());
 }
 
 bool
-BoolElement::equals(ElementPtr other) {
-    return (other->getType() == Element::boolean) &&
-           (b == other->boolValue());
+BoolElement::equals(const Element& other) const {
+    return (other.getType() == Element::boolean) &&
+           (b == other.boolValue());
 }
 
 bool
-NullElement::equals(ElementPtr other) {
-    return (other->getType() == Element::null);
+NullElement::equals(const Element& other) const {
+    return (other.getType() == Element::null);
 }
 
 bool
-StringElement::equals(ElementPtr other) {
-    return (other->getType() == Element::string) &&
-           (s == other->stringValue());
+StringElement::equals(const Element& other) const {
+    return (other.getType() == Element::string) &&
+           (s == other.stringValue());
 }
 
 bool
-ListElement::equals(ElementPtr other) {
-    if (other->getType() == Element::list) {
+ListElement::equals(const Element& other) const {
+    if (other.getType() == Element::list) {
         const int s = size();
-        if (s != other->size()) {
+        if (s != other.size()) {
             return (false);
         }
         for (int i = 0; i < s; ++i) {
-            if (!get(i)->equals(other->get(i))) {
+            if (!get(i)->equals(*other.get(i))) {
                 return (false);
             }
         }
@@ -801,13 +803,14 @@ ListElement::equals(ElementPtr other) {
 }
 
 bool
-MapElement::equals(ElementPtr other) {
-    if (other->getType() == Element::map) {
-        std::map<std::string, ElementPtr> m = mapValue();
-        for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
+MapElement::equals(const Element& other) const {
+    if (other.getType() == Element::map) {
+        const std::map<std::string, ConstElementPtr>& m = mapValue();
+        for (std::map<std::string, ConstElementPtr>::const_iterator it =
+                 m.begin();
              it != m.end() ; ++it) {
-            if (other->contains((*it).first)) {
-                if (!get((*it).first)->equals(other->get((*it).first))) {
+            if (other.contains((*it).first)) {
+                if (!get((*it).first)->equals(*other.get((*it).first))) {
                     return (false);
                 }
             } else {
@@ -819,9 +822,10 @@ MapElement::equals(ElementPtr other) {
         // compare those elements; if one of them is missing we
         // differ (and if it's not missing the loop above has checked
         // it)
-        m = other->mapValue();
-        for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
-             it != m.end() ; ++it) {
+        std::map<std::string, ConstElementPtr>::const_iterator it;
+        for (it = other.mapValue().begin();
+             it != other.mapValue().end();
+             ++it) {
             if (!contains((*it).first)) {
                 return (false);
             }
@@ -833,12 +837,12 @@ MapElement::equals(ElementPtr other) {
 }
 
 bool
-isNull(ElementPtr p) {
+isNull(ConstElementPtr p) {
     return (!p);
 }
 
 void
-removeIdentical(ElementPtr a, const ElementPtr b) {
+removeIdentical(ElementPtr a, ConstElementPtr b) {
     if (!b) {
         return;
     }
@@ -846,26 +850,50 @@ removeIdentical(ElementPtr a, const ElementPtr b) {
         isc_throw(TypeError, "Non-map Elements passed to removeIdentical");
     }
 
-    std::map<std::string, ElementPtr> m = a->mapValue();
-    for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
+    const std::map<std::string, ConstElementPtr>& m = a->mapValue();
+    for (std::map<std::string, ConstElementPtr>::const_iterator it = m.begin();
          it != m.end() ; ++it) {
         if (b->contains((*it).first)) {
-            if (a->get((*it).first)->equals(b->get((*it).first))) {
+            if (a->get((*it).first)->equals(*b->get((*it).first))) {
                 a->remove((*it).first);
             }
         }
     }
 }
 
+ConstElementPtr
+removeIdentical(ConstElementPtr a, ConstElementPtr b) {
+    ElementPtr result = Element::createMap();
+
+    if (!b) {
+        return (result);
+    }
+    
+    if (a->getType() != Element::map || b->getType() != Element::map) {
+        isc_throw(TypeError, "Non-map Elements passed to removeIdentical");
+    }
+
+    const std::map<std::string, ConstElementPtr>& m = a->mapValue();
+    for (std::map<std::string, ConstElementPtr>::const_iterator it = m.begin();
+         it != m.end() ; ++it) {
+        if (!b->contains((*it).first) ||
+            !a->get((*it).first)->equals(*b->get((*it).first))) {
+            result->set((*it).first, (*it).second);
+        }
+    }
+
+    return (result);
+}
+
 void
-merge(ElementPtr element, const ElementPtr other) {
+merge(ElementPtr element, ConstElementPtr other) {
     if (element->getType() != Element::map ||
         other->getType() != Element::map) {
         isc_throw(TypeError, "merge arguments not MapElements");
     }
     
-    std::map<std::string, ElementPtr> m = other->mapValue();
-    for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
+    std::map<std::string, ConstElementPtr> m = other->mapValue();
+    for (std::map<std::string, ConstElementPtr>::const_iterator it = m.begin();
          it != m.end() ; ++it) {
         if ((*it).second && (*it).second->getType() != Element::null) {
             element->set((*it).first, (*it).second);

+ 106 - 68
src/lib/cc/data.h

@@ -29,6 +29,7 @@ namespace isc { namespace data {
 class Element;
 // todo: describe the rationale behind ElementPtr?
 typedef boost::shared_ptr<Element> ElementPtr;
+typedef boost::shared_ptr<const Element> ConstElementPtr;
 
 ///
 /// \brief A standard Data module exception that is thrown if a function
@@ -90,7 +91,7 @@ public:
     virtual ~Element() {};
 
     /// \return the type of this element
-    int getType() { return (type); }
+    int getType() const { return (type); }
 
     /// Returns a string representing the Element and all its
     /// child elements; note that this is different from stringValue(),
@@ -99,24 +100,24 @@ public:
     /// The resulting string will contain the Element in JSON format.
     ///
     /// \return std::string containing the string representation
-    std::string str();
+    std::string str() const;
 
     /// Returns the wireformat for the Element and all its child
     /// elements.
     ///
     /// \return std::string containing the element in wire format
-    std::string toWire();
-    void toWire(std::ostream& out);
+    std::string toWire() const;
+    void toWire(std::ostream& out) const;
 
     /// \name pure virtuals, every derived class must implement these
 
     /// \returns true if the other ElementPtr has the same type and
     ///          value
-    virtual bool equals(ElementPtr other) = 0;
+    virtual bool equals(const Element& other) const = 0;
     
     /// Converts the Element to JSON format and appends it to
     /// the given stringstream.
-    virtual void toJSON(std::ostream& ss) = 0;
+    virtual void toJSON(std::ostream& ss) const = 0;
 
     /// \name Type-specific getters
     ///
@@ -126,12 +127,22 @@ public:
     /// If you want an exception-safe getter method, use
     /// getValue() below
     //@{
-    virtual long int intValue() { isc_throw(TypeError, "intValue() called on non-integer Element"); };
-    virtual double doubleValue() { isc_throw(TypeError, "doubleValue() called on non-double Element"); };
-    virtual bool boolValue() { isc_throw(TypeError, "boolValue() called on non-Bool Element"); };
-    virtual std::string stringValue() { isc_throw(TypeError, "stringValue() called on non-string Element"); };
-    virtual const std::vector<boost::shared_ptr<Element> >& listValue() { isc_throw(TypeError, "listValue() called on non-list Element"); }; // replace with real exception or empty vector?
-    virtual const std::map<std::string, boost::shared_ptr<Element> >& mapValue() { isc_throw(TypeError, "mapValue() called on non-map Element"); }; // replace with real exception or empty map?
+    virtual long int intValue() const
+    { isc_throw(TypeError, "intValue() called on non-integer Element"); };
+    virtual double doubleValue() const
+    { isc_throw(TypeError, "doubleValue() called on non-double Element"); };
+    virtual bool boolValue() const
+    { isc_throw(TypeError, "boolValue() called on non-Bool Element"); };
+    virtual std::string stringValue() const
+    { isc_throw(TypeError, "stringValue() called on non-string Element"); };
+    virtual const std::vector<ConstElementPtr>& listValue() const {
+        // replace with real exception or empty vector?
+        isc_throw(TypeError, "listValue() called on non-list Element");
+    };
+    virtual const std::map<std::string, ConstElementPtr>& mapValue() const {
+        // replace with real exception or empty map?
+        isc_throw(TypeError, "mapValue() called on non-map Element");
+    };
     //@}
 
     /// \name Exception-safe getters
@@ -147,8 +158,8 @@ public:
     virtual bool getValue(double& t);
     virtual bool getValue(bool& t);
     virtual bool getValue(std::string& t);
-    virtual bool getValue(std::vector<ElementPtr>& t);
-    virtual bool getValue(std::map<std::string, ElementPtr>& t);
+    virtual bool getValue(std::vector<ConstElementPtr>& t);
+    virtual bool getValue(std::map<std::string, ConstElementPtr>& t);
     //@}
 
     ///
@@ -163,8 +174,8 @@ public:
     virtual bool setValue(const double v);
     virtual bool setValue(const bool t);
     virtual bool setValue(const std::string& v);
-    virtual bool setValue(const std::vector<ElementPtr>& v);
-    virtual bool setValue(const std::map<std::string, ElementPtr>& v);
+    virtual bool setValue(const std::vector<ConstElementPtr>& v);
+    virtual bool setValue(const std::map<std::string, ConstElementPtr>& v);
     //@}
 
 
@@ -179,17 +190,17 @@ public:
     /// Returns the ElementPtr at the given index. If the index is out
     /// of bounds, this function throws an std::out_of_range exception.
     /// \param i The position of the ElementPtr to return
-    virtual ElementPtr get(const int i);
+    virtual ConstElementPtr get(const int i) const;
 
     /// Sets the ElementPtr at the given index. If the index is out
     /// of bounds, this function throws an std::out_of_range exception.
     /// \param i The position of the ElementPtr to set
     /// \param element The ElementPtr to set at the position
-    virtual void set(const size_t i, ElementPtr element);
+    virtual void set(const size_t i, ConstElementPtr element);
 
     /// Adds an ElementPtr to the list
     /// \param element The ElementPtr to add
-    virtual void add(ElementPtr element);
+    virtual void add(ConstElementPtr element);
 
     /// Removes the element at the given position. If the index is out
     /// of nothing happens.
@@ -197,7 +208,7 @@ public:
     virtual void remove(const int i);
 
     /// Returns the number of elements in the list.
-    virtual size_t size();
+    virtual size_t size() const;
     //@}
 
     
@@ -209,11 +220,11 @@ public:
     /// Returns the ElementPtr at the given key
     /// \param name The key of the Element to return
     /// \return The ElementPtr at the given key
-    virtual ElementPtr get(const std::string& name);
+    virtual ConstElementPtr get(const std::string& name) const;
 
     /// Sets the ElementPtr at the given key
     /// \param name The key of the Element to set
-    virtual void set(const std::string& name, ElementPtr element);
+    virtual void set(const std::string& name, ConstElementPtr element);
 
     /// Remove the ElementPtr at the given key
     /// \param name The key of the Element to remove
@@ -222,7 +233,7 @@ public:
     /// Checks if there is data at the given key
     /// \param name The key of the Element to remove
     /// \return true if there is data at the key, false if not.
-    virtual bool contains(const std::string& name);
+    virtual bool contains(const std::string& name) const;
 
     /// Recursively finds any data at the given identifier. The
     /// identifier is a /-separated list of names of nested maps, with
@@ -237,13 +248,13 @@ public:
     /// \return The ElementPtr at the given identifier. Returns a
     /// null ElementPtr if it is not found, which can be checked with
     /// Element::is_null(ElementPtr e).
-    virtual ElementPtr find(const std::string& identifier);
+    virtual ConstElementPtr find(const std::string& identifier) const;
 
     /// See \c Element::find()
     /// \param identifier The identifier of the element to find
     /// \param t Reference to store the resulting ElementPtr, if found.
     /// \return true if the element was found, false if not.
-    virtual bool find(const std::string& identifier, ElementPtr& t);
+    virtual bool find(const std::string& identifier, ConstElementPtr t) const;
     //@}
 
 
@@ -365,13 +376,13 @@ class IntElement : public Element {
 
 public:
     IntElement(long int v) : Element(integer), i(v) { }
-    long int intValue() { return (i); }
+    long int intValue() const { return (i); }
     using Element::getValue;
     bool getValue(long int& t) { t = i; return (true); }
     using Element::setValue;
     bool setValue(const long int v) { i = v; return (true); }
-    void toJSON(std::ostream& ss);
-    bool equals(ElementPtr other);
+    void toJSON(std::ostream& ss) const;
+    bool equals(const Element& other) const;
 };
 
 class DoubleElement : public Element {
@@ -379,13 +390,13 @@ class DoubleElement : public Element {
 
 public:
     DoubleElement(double v) : Element(real), d(v) {};
-    double doubleValue() { return (d); }
+    double doubleValue() const { return (d); }
     using Element::getValue;
     bool getValue(double& t) { t = d; return (true); }
     using Element::setValue;
     bool setValue(const double v) { d = v; return (true); }
-    void toJSON(std::ostream& ss);
-    bool equals(ElementPtr other);
+    void toJSON(std::ostream& ss) const;
+    bool equals(const Element& other) const;
 };
 
 class BoolElement : public Element {
@@ -393,20 +404,20 @@ class BoolElement : public Element {
 
 public:
     BoolElement(const bool v) : Element(boolean), b(v) {};
-    bool boolValue() { return (b); }
+    bool boolValue() const { return (b); }
     using Element::getValue;
     bool getValue(bool& t) { t = b; return (true); }
     using Element::setValue;
     bool setValue(const bool v) { b = v; return (true); }
-    void toJSON(std::ostream& ss);
-    bool equals(ElementPtr other);
+    void toJSON(std::ostream& ss) const;
+    bool equals(const Element& other) const;
 };
 
 class NullElement : public Element {
 public:
     NullElement() : Element(null) {};
-    void toJSON(std::ostream& ss);
-    bool equals(ElementPtr other);
+    void toJSON(std::ostream& ss) const;
+    bool equals(const Element& other) const;
 };
 
 class StringElement : public Element {
@@ -414,77 +425,97 @@ class StringElement : public Element {
 
 public:
     StringElement(std::string v) : Element(string), s(v) {};
-    std::string stringValue() { return (s); }
+    std::string stringValue() const { return (s); }
     using Element::getValue;
     bool getValue(std::string& t) { t = s; return (true); }
     using Element::setValue;
     bool setValue(const std::string& v) { s = v; return (true); }
-    void toJSON(std::ostream& ss);
-    bool equals(ElementPtr other);
+    void toJSON(std::ostream& ss) const;
+    bool equals(const Element& other) const;
 };
 
 class ListElement : public Element {
-    std::vector<ElementPtr> l;
+    std::vector<ConstElementPtr> l;
 
 public:
-    ListElement() : Element(list), l(std::vector<ElementPtr>()) {};
-    const std::vector<ElementPtr>& listValue() { return (l); }
+    ListElement() : Element(list) {}
+    const std::vector<ConstElementPtr>& listValue() const { return (l); }
     using Element::getValue;
-    bool getValue(std::vector<ElementPtr>& t) { t = l; return (true); }
+    bool getValue(std::vector<ConstElementPtr>& t) {
+        t = l;
+        return (true);
+    }
     using Element::setValue;
-    bool setValue(const std::vector<ElementPtr>& v) { l = v; return (true); }
+    bool setValue(const std::vector<ConstElementPtr>& v) {
+        l = v;
+        return (true);
+    }
     using Element::get;
-    ElementPtr get(int i) { return (l.at(i)); }
+    ConstElementPtr get(int i) const { return (l.at(i)); }
     using Element::set;
-    void set(size_t i, ElementPtr e) { if (i <= l.size()) {l[i] = e;} else { throw std::out_of_range("vector::_M_range_check"); } };
-    void add(ElementPtr e) { l.push_back(e); };
+    void set(size_t i, ConstElementPtr e) {
+        l.at(i) = e;
+    }
+    void add(ConstElementPtr e) { l.push_back(e); };
     using Element::remove;
     void remove(int i) { l.erase(l.begin() + i); };
-    void toJSON(std::ostream& ss);
-    size_t size() { return (l.size()); }
-    bool equals(ElementPtr other);
+    void toJSON(std::ostream& ss) const;
+    size_t size() const { return (l.size()); }
+    bool equals(const Element& other) const;
 };
 
 class MapElement : public Element {
-    std::map<std::string, ElementPtr> m;
+    std::map<std::string, ConstElementPtr> m;
 
 public:
-    MapElement() : Element(map), m(std::map<std::string, ElementPtr>()) {};
+    MapElement() : Element(map) {}
     // TODO: should we have direct iterators instead of exposing the std::map here?
-    const std::map<std::string, ElementPtr>& mapValue() { return (m); }
+    const std::map<std::string, ConstElementPtr>& mapValue() const {
+        return (m);
+    }
     using Element::getValue;
-    bool getValue(std::map<std::string, ElementPtr>& t) { t = m; return (true); }
+    bool getValue(std::map<std::string, ConstElementPtr>& t) {
+        t = m;
+        return (true);
+    }
     using Element::setValue;
-    bool setValue(std::map<std::string, ElementPtr>& v) { m = v; return (true); }
+    bool setValue(std::map<std::string, ConstElementPtr>& v) {
+        m = v;
+        return (true);
+    }
     using Element::get;
-    ElementPtr get(const std::string& s) { if (contains(s)) { return (m[s]); } else { return (ElementPtr());} }
+    ConstElementPtr get(const std::string& s) const {
+        return (contains(s) ? m.find(s)->second : ConstElementPtr());
+    }
     using Element::set;
-    void set(const std::string& key, ElementPtr value);
+    void set(const std::string& key, ConstElementPtr value);
     using Element::remove;
     void remove(const std::string& s) { m.erase(s); }
-    bool contains(const std::string& s) { return (m.find(s) != m.end()); }
-    void toJSON(std::ostream& ss);
+    bool contains(const std::string& s) const {
+        return (m.find(s) != m.end());
+    }
+    void toJSON(std::ostream& ss) const;
     
     // we should name the two finds better...
     // find the element at id; raises TypeError if one of the
     // elements at path except the one we're looking for is not a
     // mapelement.
     // returns an empty element if the item could not be found
-    ElementPtr find(const std::string& id);
+    ConstElementPtr find(const std::string& id) const;
 
     // find the Element at 'id', and store the element pointer in t
     // returns true if found, or false if not found (either because
     // it doesnt exist or one of the elements in the path is not
     // a MapElement)
-    bool find(const std::string& id, ElementPtr& t);
+    bool find(const std::string& id, ConstElementPtr t) const;
 
-    bool equals(ElementPtr other);
+    bool equals(const Element& other) const;
 };
 
 /// Checks whether the given ElementPtr is a NULL pointer
 /// \param p The ElementPtr to check
 /// \return true if it is NULL, false if not.
-bool isNull(ElementPtr p);
+bool isNull(ConstElementPtr p);
 
 ///
 /// \brief Remove all values from the first ElementPtr that are
@@ -493,7 +524,14 @@ bool isNull(ElementPtr p);
 /// only contains new and changed values (for ModuleCCSession and
 /// configuration update handlers)
 /// Raises a TypeError if a or b are not MapElements
-void removeIdentical(ElementPtr a, const ElementPtr b);
+void removeIdentical(ElementPtr a, ConstElementPtr b);
+
+/// \brief Create a new ElementPtr from the first ElementPtr, removing all
+/// values that are equal in the second. Both ElementPtrs MUST be MapElements.
+/// The returned ElementPtr will be a MapElement that only contains new and
+/// changed values (for ModuleCCSession and configuration update handlers).
+/// Raises a TypeError if a or b are not MapElements
+ConstElementPtr removeIdentical(ConstElementPtr a, ConstElementPtr b);
 
 /// \brief Merges the data from other into element.
 /// (on the first level). Both elements must be
@@ -507,7 +545,7 @@ void removeIdentical(ElementPtr a, const ElementPtr b);
 /// configuration data (which would then result in reverting back
 /// to the default).
 /// Raises a TypeError if either ElementPtr is not a MapElement
-void merge(ElementPtr element, const ElementPtr other);
+void merge(ElementPtr element, ConstElementPtr other);
 
 ///
 /// \brief Insert the Element as a string into stream.
@@ -524,11 +562,11 @@ void merge(ElementPtr element, const ElementPtr other);
 /// \param e The \c ElementPtr object to insert.
 /// \return A reference to the same \c std::ostream object referenced by
 /// parameter \c os after the insertion operation.
-std::ostream& operator <<(std::ostream &out, const isc::data::ElementPtr& e);
+std::ostream& operator<<(std::ostream& out, const Element& e);
 
-bool operator==(const isc::data::ElementPtr a, const isc::data::ElementPtr b);
+bool operator==(const Element& a, const Element& b);
+bool operator!=(const Element& a, const Element& b);
 } }
-
 #endif // _ISC_DATA_H
 
 // Local Variables: 

+ 18 - 16
src/lib/cc/session.cc

@@ -296,11 +296,11 @@ Session::establish(const char* socket_file) {
     //
     // send a request for our local name, and wait for a response
     //
-    ElementPtr get_lname_msg =
+    ConstElementPtr get_lname_msg =
         Element::fromJSON("{ \"type\": \"getlname\" }");
     sendmsg(get_lname_msg);
 
-    ElementPtr routing, msg;
+    ConstElementPtr routing, msg;
     recvmsg(routing, msg, false);
 
     impl_->lname_ = msg->get("lname")->stringValue();
@@ -314,7 +314,7 @@ Session::establish(const char* socket_file) {
 // prefix.
 //
 void
-Session::sendmsg(ElementPtr& msg) {
+Session::sendmsg(ConstElementPtr msg) {
     std::string header_wire = msg->toWire();
     unsigned int length = 2 + header_wire.length();
     unsigned int length_net = htonl(length);
@@ -327,7 +327,7 @@ Session::sendmsg(ElementPtr& msg) {
 }
 
 void
-Session::sendmsg(ElementPtr& env, ElementPtr& msg) {
+Session::sendmsg(ConstElementPtr env, ConstElementPtr msg) {
     std::string header_wire = env->toWire();
     std::string body_wire = msg->toWire();
     unsigned int length = 2 + header_wire.length() + body_wire.length();
@@ -342,18 +342,18 @@ Session::sendmsg(ElementPtr& env, ElementPtr& msg) {
 }
 
 bool
-Session::recvmsg(ElementPtr& msg, bool nonblock, int seq) {
-    ElementPtr l_env;
+Session::recvmsg(ConstElementPtr& msg, bool nonblock, int seq) {
+    ConstElementPtr l_env;
     return (recvmsg(l_env, msg, nonblock, seq));
 }
 
 bool
-Session::recvmsg(ElementPtr& env, ElementPtr& msg,
-                 bool nonblock, int seq) {
+Session::recvmsg(ConstElementPtr& env, ConstElementPtr& msg,
+                 bool nonblock, int seq)
+{
     size_t length = impl_->readDataLength();
-    ElementPtr l_env, l_msg;
     if (hasQueuedMsgs()) {
-        ElementPtr q_el;
+        ConstElementPtr q_el;
         for (int i = 0; i < impl_->queue_->size(); i++) {
             q_el = impl_->queue_->get(i);
             if (( seq == -1 &&
@@ -390,11 +390,13 @@ Session::recvmsg(ElementPtr& env, ElementPtr& msg,
                                         length - header_length);
     std::stringstream header_wire_stream;
     header_wire_stream << header_wire;
-    l_env = Element::fromWire(header_wire_stream, header_length);
+    ConstElementPtr l_env =
+        Element::fromWire(header_wire_stream, header_length);
     
     std::stringstream body_wire_stream;
     body_wire_stream << body_wire;
-    l_msg = Element::fromWire(body_wire_stream, length - header_length);
+    ConstElementPtr l_msg =
+        Element::fromWire(body_wire_stream, length - header_length);
     if ((seq == -1 &&
          !l_env->contains("reply")
         ) || (
@@ -438,7 +440,7 @@ Session::unsubscribe(std::string group, std::string instance) {
 }
 
 int
-Session::group_sendmsg(ElementPtr msg, std::string group,
+Session::group_sendmsg(ConstElementPtr msg, std::string group,
                        std::string instance, std::string to)
 {
     ElementPtr env = Element::createMap();
@@ -457,14 +459,14 @@ Session::group_sendmsg(ElementPtr msg, std::string group,
 }
 
 bool
-Session::group_recvmsg(ElementPtr& envelope, ElementPtr& msg,
+Session::group_recvmsg(ConstElementPtr& envelope, ConstElementPtr& msg,
                        bool nonblock, int seq)
 {
     return (recvmsg(envelope, msg, nonblock, seq));
 }
 
 int
-Session::reply(ElementPtr& envelope, ElementPtr& newmsg) {
+Session::reply(ConstElementPtr envelope, ConstElementPtr newmsg) {
     ElementPtr env = Element::createMap();
     long int nseq = ++impl_->sequence_;
     
@@ -482,7 +484,7 @@ Session::reply(ElementPtr& envelope, ElementPtr& newmsg) {
 }
 
 bool
-Session::hasQueuedMsgs() {
+Session::hasQueuedMsgs() const {
     return (impl_->queue_->size() > 0);
 }
 

+ 18 - 18
src/lib/cc/session.h

@@ -81,12 +81,12 @@ namespace isc {
             //@}
             virtual void establish(const char* socket_file) = 0;
             virtual void disconnect() = 0;
-            virtual int group_sendmsg(isc::data::ElementPtr msg,
+            virtual int group_sendmsg(isc::data::ConstElementPtr msg,
                                       std::string group,
                                       std::string instance = "*",
                                       std::string to = "*") = 0;
-            virtual bool group_recvmsg(isc::data::ElementPtr& envelope,
-                                       isc::data::ElementPtr& msg,
+            virtual bool group_recvmsg(isc::data::ConstElementPtr& envelope,
+                                       isc::data::ConstElementPtr& msg,
                                        bool nonblock = true,
                                        int seq = -1) = 0;
             virtual void subscribe(std::string group,
@@ -94,9 +94,9 @@ namespace isc {
             virtual void unsubscribe(std::string group,
                              std::string instance = "*") = 0;
             virtual void startRead(boost::function<void()> read_callback) = 0;
-            virtual int reply(isc::data::ElementPtr& envelope,
-                               isc::data::ElementPtr& newmsg) = 0;
-            virtual bool hasQueuedMsgs() = 0;
+            virtual int reply(isc::data::ConstElementPtr envelope,
+                               isc::data::ConstElementPtr newmsg) = 0;
+            virtual bool hasQueuedMsgs() const = 0;
 
             /// \brief Sets the default timeout for blocking reads
             ///        in this session to the given number of milliseconds
@@ -130,28 +130,28 @@ namespace isc {
                                    std::string instance = "*");
             virtual void unsubscribe(std::string group,
                              std::string instance = "*");
-            virtual int group_sendmsg(isc::data::ElementPtr msg,
+            virtual int group_sendmsg(isc::data::ConstElementPtr msg,
                                       std::string group,
                                       std::string instance = "*",
                                       std::string to = "*");
-            virtual bool group_recvmsg(isc::data::ElementPtr& envelope,
-                                       isc::data::ElementPtr& msg,
+            virtual bool group_recvmsg(isc::data::ConstElementPtr& envelope,
+                                       isc::data::ConstElementPtr& msg,
                                        bool nonblock = true,
                                        int seq = -1);
-            virtual int reply(isc::data::ElementPtr& envelope,
-                              isc::data::ElementPtr& newmsg);
-            virtual bool hasQueuedMsgs();
+            virtual int reply(isc::data::ConstElementPtr envelope,
+                              isc::data::ConstElementPtr newmsg);
+            virtual bool hasQueuedMsgs() const;
             virtual void setTimeout(size_t milliseconds);
             virtual size_t getTimeout() const;
     private:
-            void sendmsg(isc::data::ElementPtr& msg);
-            void sendmsg(isc::data::ElementPtr& env,
-                         isc::data::ElementPtr& msg);
-            bool recvmsg(isc::data::ElementPtr& msg,
+            void sendmsg(isc::data::ConstElementPtr msg);
+            void sendmsg(isc::data::ConstElementPtr env,
+                         isc::data::ConstElementPtr msg);
+            bool recvmsg(isc::data::ConstElementPtr& msg,
                          bool nonblock = true,
                          int seq = -1);
-            bool recvmsg(isc::data::ElementPtr& env,
-                         isc::data::ElementPtr& msg,
+            bool recvmsg(isc::data::ConstElementPtr& env,
+                         isc::data::ConstElementPtr& msg,
                          bool nonblock = true,
                          int seq = -1);
         };

+ 141 - 92
src/lib/cc/tests/data_unittests.cc

@@ -29,6 +29,7 @@ using std::setfill;
 using std::setw;
 using std::string;
 
+namespace {
 TEST(Element, type) {
     // this tests checks whether the getType() function returns the
     // correct type
@@ -73,7 +74,7 @@ TEST(Element, from_and_to_json) {
     // a set of inputs that are the same when converted to json and
     // back to a string (tests for inputs that have equivalent, but
     // different string representations when converted back are below)
-    ElementPtr el;
+    ConstElementPtr el;
     std::vector<std::string> sv;
 
     sv.push_back("12");
@@ -92,25 +93,25 @@ TEST(Element, from_and_to_json) {
     sv.push_back("-1.234");
     sv.push_back("-123.456");
     
-    BOOST_FOREACH(std::string s, sv) {
+    BOOST_FOREACH(const std::string& s, sv) {
         // test << operator, which uses Element::str()
         std::ostringstream stream;
         el = Element::fromJSON(s);
-        stream << el;
-        EXPECT_EQ(stream.str(), s);
+        stream << *el;
+        EXPECT_EQ(s, stream.str());
 
         // test toWire(ostream), which should also be the same now
         std::ostringstream wire_stream;
         el->toWire(wire_stream);
-        EXPECT_EQ(wire_stream.str(), s);
+        EXPECT_EQ(s, wire_stream.str());
     }
 
     // some parse errors
     try {
         Element::fromJSON("{1}");
-    } catch (isc::data::JSONError pe) {
+    } catch (const isc::data::JSONError& pe) {
         std::string s = std::string(pe.what());
-        EXPECT_EQ(s, "String expected in <string>:1:3");
+        EXPECT_EQ("String expected in <string>:1:3", s);
     }
     
     sv.clear();
@@ -169,9 +170,8 @@ TEST(Element, create_and_value_throws) {
     double d;
     bool b;
     std::string s("asdf");
-    std::vector<ElementPtr> v;
-    std::map<std::string, ElementPtr> m;
-    
+    std::vector<ConstElementPtr> v;
+    std::map<std::string, ConstElementPtr> m;
 
     el = Element::create(1);
     EXPECT_NO_THROW(el->intValue());
@@ -204,7 +204,7 @@ TEST(Element, create_and_value_throws) {
     EXPECT_THROW(el->set("foo", el), TypeError);
     EXPECT_THROW(el->remove("foo"), TypeError);
     EXPECT_THROW(el->contains("foo"), TypeError);
-    ElementPtr tmp;
+    ConstElementPtr tmp;
     EXPECT_FALSE(el->find("foo", tmp));
     
 
@@ -322,13 +322,19 @@ TEST(Element, ListElement) {
     EXPECT_ANY_THROW(el->get(3));
 
     el->add(Element::create(32));
-    EXPECT_EQ(el->get(2)->intValue(), 32);
+    EXPECT_EQ(32, el->get(2)->intValue());
+
+    // boundary condition tests for set()
+    el->set(2, Element::create(0)); // update the last entry of the list
+    EXPECT_EQ(0, el->get(2)->intValue());
+    // attempt of set beyond the range of list should trigger an exception.
+    EXPECT_ANY_THROW(el->set(3, Element::create(0)));
 }
 
 TEST(Element, MapElement) {
     // this function checks the specific functions for ListElements
     ElementPtr el = Element::fromJSON("{ \"name\": \"foo\", \"value1\": \"bar\", \"value2\": { \"number\": 42 } }");
-    ElementPtr el2;
+    ConstElementPtr el2;
     
     EXPECT_EQ(el->get("name")->stringValue(), "foo");
     EXPECT_EQ(el->get("value2")->getType(), Element::map);
@@ -376,7 +382,6 @@ TEST(Element, MapElement) {
 
 TEST(Element, to_and_from_wire) {
     // Wire format is now plain JSON.
-    ElementPtr el;
     EXPECT_EQ("1", Element::create(1)->toWire());
     EXPECT_EQ("1.1", Element::create(1.1)->toWire());
     EXPECT_EQ("true", Element::create(true)->toWire());
@@ -398,124 +403,167 @@ TEST(Element, to_and_from_wire) {
     EXPECT_THROW(Element::fromJSON("[ 1, 2, }"), isc::data::JSONError);
 }
 
-static ElementPtr
+ConstElementPtr
 efs(const std::string& str) {
     return (Element::fromJSON(str));
 }
 
 TEST(Element, equals) {
-    // why does EXPECT_EQ not work?
-    EXPECT_EQ(efs("1"), efs("1"));
-    EXPECT_NE(efs("1"), efs("2"));
-    EXPECT_NE(efs("1"), efs("\"1\""));
-    EXPECT_NE(efs("1"), efs("[]"));
-    EXPECT_NE(efs("1"), efs("True"));
-    EXPECT_NE(efs("1"), efs("{}"));
-
-    EXPECT_EQ(efs("1.1"), efs("1.1"));
-    EXPECT_NE(efs("1.0"), efs("1"));
-    EXPECT_NE(efs("1.1"), efs("\"1\""));
-    EXPECT_NE(efs("1.1"), efs("[]"));
-    EXPECT_NE(efs("1.1"), efs("True"));
-    EXPECT_NE(efs("1.1"), efs("{}"));
-
-    EXPECT_EQ(efs("True"), efs("True"));
-    EXPECT_NE(efs("True"), efs("False"));
-    EXPECT_NE(efs("True"), efs("1"));
-    EXPECT_NE(efs("True"), efs("\"1\""));
-    EXPECT_NE(efs("True"), efs("[]"));
-    EXPECT_NE(efs("True"), efs("{}"));
-
-    EXPECT_EQ(efs("\"foo\""), efs("\"foo\""));
-    EXPECT_NE(efs("\"foo\""), efs("\"bar\""));
-    EXPECT_NE(efs("\"foo\""), efs("1"));
-    EXPECT_NE(efs("\"foo\""), efs("\"1\""));
-    EXPECT_NE(efs("\"foo\""), efs("True"));
-    EXPECT_NE(efs("\"foo\""), efs("[]"));
-    EXPECT_NE(efs("\"foo\""), efs("{}"));
-
-    EXPECT_EQ(efs("[]"), efs("[]"));
-    EXPECT_EQ(efs("[ 1, 2, 3 ]"), efs("[ 1, 2, 3 ]"));
-    EXPECT_EQ(efs("[ \"a\", [ True, 1], 2.2 ]"), efs("[ \"a\", [ True, 1], 2.2 ]"));
-    EXPECT_NE(efs("[ \"a\", [ True, 1], 2.2 ]"), efs("[ \"a\", [ True, 2], 2.2 ]"));
-    EXPECT_NE(efs("[]"), efs("[1]"));
-    EXPECT_NE(efs("[]"), efs("1"));
-    EXPECT_NE(efs("[]"), efs("\"1\""));
-    EXPECT_NE(efs("[]"), efs("{}"));
-
-    EXPECT_EQ(efs("{}"), efs("{}"));
-    EXPECT_EQ(efs("{ \"foo\": \"bar\" }"), efs("{ \"foo\": \"bar\" }"));
-    EXPECT_EQ(efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar\" } }"), efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar\" } }"));
-    EXPECT_NE(efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar\" } }"), efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar2\" } }"));
-    EXPECT_NE(efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar\" } }"), efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\", 1 ], \"item3\": { \"foo\": \"bar\" } }"));
-    EXPECT_NE(efs("{ \"foo\": \"bar\" }"), efs("1"));
-    EXPECT_NE(efs("{ \"foo\": \"bar\" }"), efs("\"1\""));
-    EXPECT_NE(efs("{ \"foo\": \"bar\" }"), efs("[]"));
-    EXPECT_NE(efs("{ \"foo\": \"bar\" }"), efs("{}"));
-    EXPECT_NE(efs("{ \"foo\": \"bar\" }"), efs("{ \"something\": \"different\" }"));
-
-    EXPECT_EQ(efs("null"), Element::create());
+    EXPECT_EQ(*efs("1"), *efs("1"));
+    EXPECT_NE(*efs("1"), *efs("2"));
+    EXPECT_NE(*efs("1"), *efs("\"1\""));
+    EXPECT_NE(*efs("1"), *efs("[]"));
+    EXPECT_NE(*efs("1"), *efs("True"));
+    EXPECT_NE(*efs("1"), *efs("{}"));
+
+    EXPECT_EQ(*efs("1.1"), *efs("1.1"));
+    EXPECT_NE(*efs("1.0"), *efs("1"));
+    EXPECT_NE(*efs("1.1"), *efs("\"1\""));
+    EXPECT_NE(*efs("1.1"), *efs("[]"));
+    EXPECT_NE(*efs("1.1"), *efs("True"));
+    EXPECT_NE(*efs("1.1"), *efs("{}"));
+
+    EXPECT_EQ(*efs("True"), *efs("True"));
+    EXPECT_NE(*efs("True"), *efs("False"));
+    EXPECT_NE(*efs("True"), *efs("1"));
+    EXPECT_NE(*efs("True"), *efs("\"1\""));
+    EXPECT_NE(*efs("True"), *efs("[]"));
+    EXPECT_NE(*efs("True"), *efs("{}"));
+
+    EXPECT_EQ(*efs("\"foo\""), *efs("\"foo\""));
+    EXPECT_NE(*efs("\"foo\""), *efs("\"bar\""));
+    EXPECT_NE(*efs("\"foo\""), *efs("1"));
+    EXPECT_NE(*efs("\"foo\""), *efs("\"1\""));
+    EXPECT_NE(*efs("\"foo\""), *efs("True"));
+    EXPECT_NE(*efs("\"foo\""), *efs("[]"));
+    EXPECT_NE(*efs("\"foo\""), *efs("{}"));
+
+    EXPECT_EQ(*efs("[]"), *efs("[]"));
+    EXPECT_EQ(*efs("[ 1, 2, 3 ]"), *efs("[ 1, 2, 3 ]"));
+    EXPECT_EQ(*efs("[ \"a\", [ True, 1], 2.2 ]"), *efs("[ \"a\", [ True, 1], 2.2 ]"));
+    EXPECT_NE(*efs("[ \"a\", [ True, 1], 2.2 ]"), *efs("[ \"a\", [ True, 2], 2.2 ]"));
+    EXPECT_NE(*efs("[]"), *efs("[1]"));
+    EXPECT_NE(*efs("[]"), *efs("1"));
+    EXPECT_NE(*efs("[]"), *efs("\"1\""));
+    EXPECT_NE(*efs("[]"), *efs("{}"));
+
+    EXPECT_EQ(*efs("{}"), *efs("{}"));
+    EXPECT_EQ(*efs("{ \"foo\": \"bar\" }"), *efs("{ \"foo\": \"bar\" }"));
+    EXPECT_EQ(*efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar\" } }"), *efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar\" } }"));
+    EXPECT_NE(*efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar\" } }"), *efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar2\" } }"));
+    EXPECT_NE(*efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar\" } }"), *efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\", 1 ], \"item3\": { \"foo\": \"bar\" } }"));
+    EXPECT_NE(*efs("{ \"foo\": \"bar\" }"), *efs("1"));
+    EXPECT_NE(*efs("{ \"foo\": \"bar\" }"), *efs("\"1\""));
+    EXPECT_NE(*efs("{ \"foo\": \"bar\" }"), *efs("[]"));
+    EXPECT_NE(*efs("{ \"foo\": \"bar\" }"), *efs("{}"));
+    EXPECT_NE(*efs("{ \"foo\": \"bar\" }"), *efs("{ \"something\": \"different\" }"));
+
+    EXPECT_EQ(*efs("null"), *Element::create());
 }
 
 TEST(Element, removeIdentical) {
     ElementPtr a = Element::createMap();
-    ElementPtr b = Element::createMap();
-    ElementPtr c = Element::createMap();
+    ConstElementPtr b = Element::createMap();
+    ConstElementPtr c = Element::createMap();
     removeIdentical(a, b);
-    EXPECT_EQ(a, c);
+    EXPECT_EQ(*a, *c);
 
     a = Element::fromJSON("{ \"a\": 1 }");
     b = Element::fromJSON("{ \"a\": 1 }");
     c = Element::createMap();
     removeIdentical(a, b);
-    EXPECT_EQ(a, c);
+    EXPECT_EQ(*a, *c);
 
     a = Element::fromJSON("{ \"a\": 1, \"b\": [ 1, 2 ] }");
     b = Element::createMap();
     c = Element::fromJSON("{ \"a\": 1, \"b\": [ 1, 2 ] }");
     removeIdentical(a, b);
-    EXPECT_EQ(a, c);
+    EXPECT_EQ(*a, *c);
 
     a = Element::fromJSON("{ \"a\": 1, \"b\": [ 1, 2 ] }");
     b = Element::fromJSON("{ \"a\": 1, \"b\": [ 1, 2 ] }");
     c = Element::createMap();
     removeIdentical(a, b);
-    EXPECT_EQ(a, c);
+    EXPECT_EQ(*a, *c);
 
     a = Element::fromJSON("{ \"a\": 1, \"b\": [ 1, 2 ] }");
     b = Element::fromJSON("{ \"a\": 1, \"b\": [ 1, 3 ] }");
     c = Element::fromJSON("{ \"b\": [ 1, 2 ] }");
     removeIdentical(a, b);
-    EXPECT_EQ(a, c);
+    EXPECT_EQ(*a, *c);
 
     a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
     b = Element::createMap();
     c = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
     removeIdentical(a, b);
-    EXPECT_EQ(a, c);
+    EXPECT_EQ(*a, *c);
 
     a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
     b = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
     c = Element::createMap();
     removeIdentical(a, b);
-    EXPECT_EQ(a, c);
+    EXPECT_EQ(*a, *c);
 
     a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
     b = Element::fromJSON("{ \"a\": { \"b\": \"d\" } }");
     c = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
     removeIdentical(a, b);
-    EXPECT_EQ(a, c);
+    EXPECT_EQ(*a, *c);
 
     EXPECT_THROW(removeIdentical(Element::create(1), Element::create(2)), TypeError);
 }
 
-TEST(Element, merge)
-{
+TEST(Element, constRemoveIdentical) {
+    ConstElementPtr a = Element::createMap();
+    ConstElementPtr b = Element::createMap();
+    ConstElementPtr c = Element::createMap();
+    EXPECT_EQ(*removeIdentical(a, b), *c);
+
+    a = Element::fromJSON("{ \"a\": 1 }");
+    b = Element::fromJSON("{ \"a\": 1 }");
+    c = Element::createMap();
+    EXPECT_EQ(*removeIdentical(a, b), *c);
+
+    a = Element::fromJSON("{ \"a\": 1, \"b\": [ 1, 2 ] }");
+    b = Element::createMap();
+    c = Element::fromJSON("{ \"a\": 1, \"b\": [ 1, 2 ] }");
+    EXPECT_EQ(*removeIdentical(a, b), *c);
+
+    a = Element::fromJSON("{ \"a\": 1, \"b\": [ 1, 2 ] }");
+    b = Element::fromJSON("{ \"a\": 1, \"b\": [ 1, 2 ] }");
+    c = Element::createMap();
+    EXPECT_EQ(*removeIdentical(a, b), *c);
+
+    a = Element::fromJSON("{ \"a\": 1, \"b\": [ 1, 2 ] }");
+    b = Element::fromJSON("{ \"a\": 1, \"b\": [ 1, 3 ] }");
+    c = Element::fromJSON("{ \"b\": [ 1, 2 ] }");
+    EXPECT_EQ(*removeIdentical(a, b), *c);
+
+    a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    b = Element::createMap();
+    c = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    EXPECT_EQ(*removeIdentical(a, b), *c);
+
+    a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    b = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    c = Element::createMap();
+    EXPECT_EQ(*removeIdentical(a, b), *c);
+
+    a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    b = Element::fromJSON("{ \"a\": { \"b\": \"d\" } }");
+    c = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    EXPECT_EQ(*removeIdentical(a, b), *c);
+
+    EXPECT_THROW(removeIdentical(Element::create(1), Element::create(2)),
+                 TypeError);
+}
+
+TEST(Element, merge) {
     ElementPtr a = Element::createMap();
     ElementPtr b = Element::createMap();
-    ElementPtr c = Element::createMap();
+    ConstElementPtr c = Element::createMap();
     merge(a, b);
-    EXPECT_EQ(a, c);
+    EXPECT_EQ(*a, *c);
 
     a = Element::fromJSON("1");
     b = Element::createMap();
@@ -525,73 +573,74 @@ TEST(Element, merge)
     b = Element::fromJSON("{ \"a\": 1 }");
     c = Element::fromJSON("{ \"a\": 1 }");
     merge(a, b);
-    EXPECT_EQ(a, c);
+    EXPECT_EQ(*a, *c);
 
     a = Element::createMap();
     b = Element::fromJSON("{ \"a\": 1 }");
     c = Element::fromJSON("{ \"a\": 1 }");
     merge(b, a);
-    EXPECT_EQ(b, c);
+    EXPECT_EQ(*b, *c);
 
     a = Element::fromJSON("{ \"a\": 1 }");
     b = Element::fromJSON("{ \"a\": 2 }");
     c = Element::fromJSON("{ \"a\": 2 }");
     merge(a, b);
-    EXPECT_EQ(a, c);
+    EXPECT_EQ(*a, *c);
 
     a = Element::fromJSON("{ \"a\": 1 }");
     b = Element::fromJSON("{ \"a\": 2 }");
     c = Element::fromJSON("{ \"a\": 1 }");
     merge(b, a);
-    EXPECT_EQ(b, c);
+    EXPECT_EQ(*b, *c);
 
     a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
     b = Element::fromJSON("{ \"a\": { \"b\": \"d\" } }");
     c = Element::fromJSON("{ \"a\": { \"b\": \"d\" } }");
     merge(a, b);
-    EXPECT_EQ(a, c);
+    EXPECT_EQ(*a, *c);
 
     a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
     b = Element::fromJSON("{ \"a\": { \"b\": \"d\" } }");
     c = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
     merge(b, a);
-    EXPECT_EQ(b, c);
+    EXPECT_EQ(*b, *c);
 
     a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
     b = Element::fromJSON("{ \"a\": null }");
     c = Element::fromJSON("{  }");
     merge(a, b);
-    EXPECT_EQ(a, c);
+    EXPECT_EQ(*a, *c);
 
     a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
     b = Element::fromJSON("{ \"a\": null }");
     c = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
     merge(b, a);
-    EXPECT_EQ(b, c);
+    EXPECT_EQ(*b, *c);
     
     // And some tests with multiple values
     a = Element::fromJSON("{ \"a\": 1, \"b\": true, \"c\": null }");
     b = Element::fromJSON("{ \"a\": 1, \"b\": null, \"c\": \"a string\" }");
     c = Element::fromJSON("{ \"a\": 1, \"c\": \"a string\" }");
     merge(a, b);
-    EXPECT_EQ(a, c);
+    EXPECT_EQ(*a, *c);
 
     a = Element::fromJSON("{ \"a\": 1, \"b\": true, \"c\": null }");
     b = Element::fromJSON("{ \"a\": 1, \"b\": null, \"c\": \"a string\" }");
     c = Element::fromJSON("{ \"a\": 1, \"b\": true }");
     merge(b, a);
-    EXPECT_EQ(b, c);
+    EXPECT_EQ(*b, *c);
 
     a = Element::fromJSON("{ \"a\": 1, \"b\": 2, \"c\": 3 }");
     b = Element::fromJSON("{ \"a\": 3, \"b\": 2, \"c\": 1 }");
     c = Element::fromJSON("{ \"a\": 3, \"b\": 2, \"c\": 1 }");
     merge(a, b);
-    EXPECT_EQ(a, c);
+    EXPECT_EQ(*a, *c);
 
     a = Element::fromJSON("{ \"a\": 1, \"b\": 2, \"c\": 3 }");
     b = Element::fromJSON("{ \"a\": 3, \"b\": 2, \"c\": 1 }");
     c = Element::fromJSON("{ \"a\": 1, \"b\": 2, \"c\": 3 }");
     merge(b, a);
-    EXPECT_EQ(b, c);
+    EXPECT_EQ(*b, *c);
 
 }
+}

+ 2 - 2
src/lib/cc/tests/session_unittests.cc

@@ -140,7 +140,7 @@ public:
     // If this message is { "command": "stop" } it'll tell the
     // io_service it is done. Otherwise it'll re-register this handler
     void someHandler() {
-        isc::data::ElementPtr env, msg;
+        isc::data::ConstElementPtr env, msg;
         sess.group_recvmsg(env, msg, false, -1);
 
         sess.group_recvmsg(env, msg, false, -1);
@@ -188,7 +188,7 @@ TEST_F(SessionTest, connect_ok_connection_reset) {
     // Close the session again, so the next recv() should throw
     sess.disconnect();
 
-    isc::data::ElementPtr env, msg;
+    isc::data::ConstElementPtr env, msg;
     EXPECT_THROW(sess.group_recvmsg(env, msg, false, -1), SessionError);
 }
 

+ 68 - 57
src/lib/config/ccsession.cc

@@ -52,41 +52,47 @@ namespace isc {
 namespace config {
 
 /// Creates a standard config/command protocol answer message
-ElementPtr
+ConstElementPtr
 createAnswer() {
     ElementPtr answer = Element::fromJSON("{\"result\": [] }");
-    ElementPtr answer_content = answer->get("result");
+    ElementPtr answer_content = Element::createList();
     answer_content->add(Element::create(0));
+    answer->set("result", answer_content);
+
     return (answer);
 }
 
-ElementPtr
-createAnswer(const int rcode, const ElementPtr arg) {
+ConstElementPtr
+createAnswer(const int rcode, ConstElementPtr arg) {
     if (rcode != 0 && (!arg || arg->getType() != Element::string)) {
         isc_throw(CCSessionError, "Bad or no argument for rcode != 0");
     }
     ElementPtr answer = Element::fromJSON("{\"result\": [] }");
-    ElementPtr answer_content = answer->get("result");
+    ElementPtr answer_content = Element::createList();
     answer_content->add(Element::create(rcode));
     answer_content->add(arg);
+    answer->set("result", answer_content);
+
     return (answer);
 }
 
-ElementPtr
+ConstElementPtr
 createAnswer(const int rcode, const std::string& arg) {
     ElementPtr answer = Element::fromJSON("{\"result\": [] }");
-    ElementPtr answer_content = answer->get("result");
+    ElementPtr answer_content = Element::createList();
     answer_content->add(Element::create(rcode));
     answer_content->add(Element::create(arg));
+    answer->set("result", answer_content);
+
     return (answer);
 }
 
-ElementPtr
-parseAnswer(int &rcode, const ElementPtr msg) {
+ConstElementPtr
+parseAnswer(int &rcode, ConstElementPtr msg) {
     if (msg &&
         msg->getType() == Element::map &&
         msg->contains("result")) {
-        ElementPtr result = msg->get("result");
+        ConstElementPtr result = msg->get("result");
         if (result->getType() != Element::list) {
             isc_throw(CCSessionError, "Result element in answer message is not a list");
         } else if (result->get(0)->getType() != Element::integer) {
@@ -111,13 +117,13 @@ parseAnswer(int &rcode, const ElementPtr msg) {
     }
 }
 
-ElementPtr
+ConstElementPtr
 createCommand(const std::string& command) {
     return (createCommand(command, ElementPtr()));
 }
 
-ElementPtr
-createCommand(const std::string& command, ElementPtr arg) {
+ConstElementPtr
+createCommand(const std::string& command, ConstElementPtr arg) {
     ElementPtr cmd = Element::createMap();
     ElementPtr cmd_parts = Element::createList();
     cmd_parts->add(Element::create(command));
@@ -130,13 +136,12 @@ createCommand(const std::string& command, ElementPtr arg) {
 
 /// Returns "" and empty ElementPtr() if this does not
 /// look like a command
-const std::string
-parseCommand(ElementPtr& arg, const ElementPtr command)
-{
+std::string
+parseCommand(ConstElementPtr& arg, ConstElementPtr command) {
     if (command &&
         command->getType() == Element::map &&
         command->contains("command")) {
-        ElementPtr cmd = command->get("command");
+        ConstElementPtr cmd = command->get("command");
         if (cmd->getType() == Element::list &&
             cmd->size() > 0 &&
             cmd->get(0)->getType() == Element::string) {
@@ -192,9 +197,10 @@ ModuleCCSession::startCheck() {
 ModuleCCSession::ModuleCCSession(
     const std::string& spec_file_name,
     isc::cc::AbstractSession& session,
-    isc::data::ElementPtr(*config_handler)(isc::data::ElementPtr new_config),
-    isc::data::ElementPtr(*command_handler)(
-        const std::string& command, const isc::data::ElementPtr args)
+    isc::data::ConstElementPtr(*config_handler)(
+        isc::data::ConstElementPtr new_config),
+    isc::data::ConstElementPtr(*command_handler)(
+        const std::string& command, isc::data::ConstElementPtr args)
     ) :
     session_(session)
 {
@@ -205,18 +211,20 @@ ModuleCCSession::ModuleCCSession(
     config_handler_ = config_handler;
     command_handler_ = command_handler;
 
-    ElementPtr answer, env;
-
     session_.establish(NULL);
     session_.subscribe(module_name_, "*");
     //session_.subscribe("Boss", "*");
     //session_.subscribe("statistics", "*");
     // send the data specification
-    ElementPtr spec_msg = createCommand("module_spec", module_specification_.getFullSpec());
+
+    ConstElementPtr spec_msg = createCommand("module_spec",
+                                             module_specification_.getFullSpec());
     unsigned int seq = session_.group_sendmsg(spec_msg, "ConfigManager");
+
+    ConstElementPtr answer, env;
     session_.group_recvmsg(env, answer, false, seq);
     int rcode;
-    ElementPtr err = parseAnswer(rcode, answer);
+    ConstElementPtr err = parseAnswer(rcode, answer);
     if (rcode != 0) {
         std::cerr << "[" << module_name_ << "] Error in specification: " << answer << std::endl;
     }
@@ -224,10 +232,10 @@ ModuleCCSession::ModuleCCSession(
     setLocalConfig(Element::fromJSON("{}"));
     // get any stored configuration from the manager
     if (config_handler_) {
-        ElementPtr cmd = Element::fromJSON("{ \"command\": [\"get_config\", {\"module_name\":\"" + module_name_ + "\"} ] }");
+        ConstElementPtr cmd = Element::fromJSON("{ \"command\": [\"get_config\", {\"module_name\":\"" + module_name_ + "\"} ] }");
         seq = session_.group_sendmsg(cmd, "ConfigManager");
         session_.group_recvmsg(env, answer, false, seq);
-        ElementPtr new_config = parseAnswer(rcode, answer);
+        ConstElementPtr new_config = parseAnswer(rcode, answer);
         if (rcode == 0) {
             handleConfigUpdate(new_config);
         } else {
@@ -242,30 +250,30 @@ ModuleCCSession::ModuleCCSession(
 /// Validates the new config values, if they are correct,
 /// call the config handler with the values that have changed
 /// If that results in success, store the new config
-ElementPtr
-ModuleCCSession::handleConfigUpdate(ElementPtr new_config)
-{
-    ElementPtr answer;
+ConstElementPtr
+ModuleCCSession::handleConfigUpdate(ConstElementPtr new_config) {
+    ConstElementPtr answer;
     ElementPtr errors = Element::createList();
     if (!config_handler_) {
         answer = createAnswer(1, module_name_ + " does not have a config handler");
-    } else if (!module_specification_.validate_config(new_config, false, errors)) {
+    } else if (!module_specification_.validate_config(new_config, false,
+                                                      errors)) {
         std::stringstream ss;
         ss << "Error in config validation: ";
-        BOOST_FOREACH(ElementPtr error, errors->listValue()) {
+        BOOST_FOREACH(ConstElementPtr error, errors->listValue()) {
             ss << error->stringValue();
         }
         answer = createAnswer(2, ss.str());
     } else {
         // remove the values that have not changed
-        isc::data::removeIdentical(new_config, getLocalConfig());
+        ConstElementPtr diff = removeIdentical(new_config, getLocalConfig());
         // handle config update
-        answer = config_handler_(new_config);
+        answer = config_handler_(diff);
         int rcode;
         parseAnswer(rcode, answer);
         if (rcode == 0) {
             ElementPtr local_config = getLocalConfig();
-            isc::data::merge(local_config, new_config);
+            isc::data::merge(local_config, diff);
             setLocalConfig(local_config);
         }
     }
@@ -273,15 +281,13 @@ ModuleCCSession::handleConfigUpdate(ElementPtr new_config)
 }
 
 bool
-ModuleCCSession::hasQueuedMsgs()
-{
+ModuleCCSession::hasQueuedMsgs() const {
     return (session_.hasQueuedMsgs());
 }
 
 int
-ModuleCCSession::checkCommand()
-{
-    ElementPtr cmd, routing, data;
+ModuleCCSession::checkCommand() {
+    ConstElementPtr cmd, routing, data;
     if (session_.group_recvmsg(routing, data, true)) {
         
         /* ignore result messages (in case we're out of sync, to prevent
@@ -289,8 +295,8 @@ ModuleCCSession::checkCommand()
         if (data->getType() != Element::map || data->contains("result")) {
             return (0);
         }
-        ElementPtr arg;
-        ElementPtr answer;
+        ConstElementPtr arg;
+        ConstElementPtr answer;
         try {
             std::string cmd_str = parseCommand(arg, data);
             std::string target_module = routing->get("group")->stringValue();
@@ -313,7 +319,7 @@ ModuleCCSession::checkCommand()
                     }
                 }
             }
-        } catch (CCSessionError re) {
+        } catch (const CCSessionError& re) {
             // TODO: Once we have logging and timeouts, we should not
             // answer here (potential interference)
             answer = createAnswer(1, re.what());
@@ -335,15 +341,17 @@ ModuleCCSession::addRemoteConfig(const std::string& spec_file_name)
     session_.subscribe(module_name);
 
     // Get the current configuration values for that module
-    ElementPtr cmd = Element::fromJSON("{ \"command\": [\"get_config\", {\"module_name\":\"" + module_name + "\"} ] }");
-    ElementPtr env, answer;
-    int rcode;
-    
+    ConstElementPtr cmd = Element::fromJSON("{ \"command\": [\"get_config\", {\"module_name\":\"" + module_name + "\"} ] }");
     unsigned int seq = session_.group_sendmsg(cmd, "ConfigManager");
+
+    ConstElementPtr env, answer;
     session_.group_recvmsg(env, answer, false, seq);
-    ElementPtr new_config = parseAnswer(rcode, answer);
-    if (rcode == 0) {
-        rmod_config.setLocalConfig(new_config);
+    int rcode;
+    ConstElementPtr new_config = parseAnswer(rcode, answer);
+    if (rcode == 0 && new_config) {
+        ElementPtr local_config = rmod_config.getLocalConfig();
+        isc::data::merge(local_config, new_config);
+        rmod_config.setLocalConfig(local_config);
     } else {
         isc_throw(CCSessionError, "Error getting config for " + module_name + ": " + answer->str());
     }
@@ -365,21 +373,24 @@ ModuleCCSession::removeRemoteConfig(const std::string& module_name)
     }
 }
 
-ElementPtr
-ModuleCCSession::getRemoteConfigValue(const std::string& module_name, const std::string& identifier)
+ConstElementPtr
+ModuleCCSession::getRemoteConfigValue(const std::string& module_name,
+                                      const std::string& identifier) const
 {
-    std::map<std::string, ConfigData>::iterator it;
+    std::map<std::string, ConfigData>::const_iterator it =
+        remote_module_configs_.find(module_name);
 
-    it = remote_module_configs_.find(module_name);
     if (it != remote_module_configs_.end()) {
-        return (remote_module_configs_[module_name].getValue(identifier));
+        return ((*it).second.getValue(identifier));
     } else {
-        isc_throw(CCSessionError, "Remote module " + module_name + " not found.");
+        isc_throw(CCSessionError,
+                  "Remote module " + module_name + " not found.");
     }
 }
 
 void
-ModuleCCSession::updateRemoteConfig(const std::string& module_name, ElementPtr new_config)
+ModuleCCSession::updateRemoteConfig(const std::string& module_name,
+                                    ConstElementPtr new_config)
 {
     std::map<std::string, ConfigData>::iterator it;
 

+ 33 - 20
src/lib/config/ccsession.h

@@ -31,7 +31,7 @@ namespace config {
 /// \brief Creates a standard config/command level success answer message
 ///        (i.e. of the form { "result": [ 0 ] }
 /// \return Standard command/config success answer message
-ElementPtr createAnswer();
+ConstElementPtr createAnswer();
 
 ///
 /// \brief Creates a standard config/command level answer message
@@ -43,7 +43,7 @@ ElementPtr createAnswer();
 ///            Element type. For rcode == 1, this argument is mandatory,
 ///            and must be a StringElement containing an error description
 /// \return Standard command/config answer message
-ElementPtr createAnswer(const int rcode, const ElementPtr arg);
+ConstElementPtr createAnswer(const int rcode, ConstElementPtr arg);
 
 ///
 /// \brief Creates a standard config/command level answer message
@@ -52,7 +52,7 @@ ElementPtr createAnswer(const int rcode, const ElementPtr arg);
 /// \param rcode The return code (0 for success)
 /// \param arg A string to put into the StringElement argument
 /// \return Standard command/config answer message
-ElementPtr createAnswer(const int rcode, const std::string& arg);
+ConstElementPtr createAnswer(const int rcode, const std::string& arg);
 
 ///
 /// Parses a standard config/command level answer message
@@ -63,8 +63,7 @@ ElementPtr createAnswer(const int rcode, const std::string& arg);
 /// \return The optional argument in the message, or an empty ElementPtr
 ///         if there was no argument. If rcode != 0, this contains a
 ///         StringElement with the error description.
-ElementPtr parseAnswer(int &rcode, const ElementPtr msg);
-
+ConstElementPtr parseAnswer(int &rcode, ConstElementPtr msg);
 
 ///
 /// \brief Creates a standard config/command command message with no
@@ -72,7 +71,7 @@ ElementPtr parseAnswer(int &rcode, const ElementPtr msg);
 /// 
 /// \param command The command string
 /// \return The created message
-ElementPtr createCommand(const std::string& command);
+ConstElementPtr createCommand(const std::string& command);
 
 ///
 /// \brief Creates a standard config/command command message with the
@@ -82,7 +81,7 @@ ElementPtr createCommand(const std::string& command);
 /// \param arg The optional argument for the command. This can be of 
 ///        any Element type, but it should conform to the .spec file.
 /// \return The created message
-ElementPtr createCommand(const std::string& command, ElementPtr arg);
+ConstElementPtr createCommand(const std::string& command, ConstElementPtr arg);
 
 ///
 /// \brief Parses the given command into a string containing the actual
@@ -93,7 +92,7 @@ ElementPtr createCommand(const std::string& command, ElementPtr arg);
 /// \param command The command message containing the command (as made
 ///        by createCommand()
 /// \return The command string
-const std::string parseCommand(ElementPtr& arg, const ElementPtr command);
+std::string parseCommand(ConstElementPtr& arg, ConstElementPtr command);
 
 
 ///
@@ -133,11 +132,11 @@ public:
      */
     ModuleCCSession(const std::string& spec_file_name,
                     isc::cc::AbstractSession& session,
-                    isc::data::ElementPtr(*config_handler)(
-                        isc::data::ElementPtr new_config) = NULL,
-                    isc::data::ElementPtr(*command_handler)(
+                    isc::data::ConstElementPtr(*config_handler)(
+                        isc::data::ConstElementPtr new_config) = NULL,
+                    isc::data::ConstElementPtr(*command_handler)(
                         const std::string& command,
-                        const isc::data::ElementPtr args) = NULL
+                        isc::data::ConstElementPtr args) = NULL
                     );
 
     /**
@@ -148,7 +147,7 @@ public:
      * 
      * @return true if there are unhandled queued messages
      */
-    bool hasQueuedMsgs();
+    bool hasQueuedMsgs() const;
 
     /**
      * Check if there is a command or config change on the command
@@ -167,7 +166,11 @@ public:
      * 100000 zones, where the whole list is passed every time a single
      * thing changes)
      */
-    void setConfigHandler(isc::data::ElementPtr(*config_handler)(isc::data::ElementPtr new_config)) { config_handler_ = config_handler; };
+    void setConfigHandler(isc::data::ConstElementPtr(*config_handler)(
+                              isc::data::ConstElementPtr new_config))
+    {
+        config_handler_ = config_handler;
+    }
 
     /**
      * Set a command handler; the function that is passed takes an
@@ -179,7 +182,12 @@ public:
      *
      * This protocol is very likely to change.
      */
-    void setCommandHandler(isc::data::ElementPtr(*command_handler)(const std::string& command, const isc::data::ElementPtr args)) { command_handler_ = command_handler; };
+    void setCommandHandler(isc::data::ConstElementPtr(*command_handler)(
+                               const std::string& command,
+                               isc::data::ConstElementPtr args))
+    {
+        command_handler_ = command_handler;
+    }
 
     /**
      * Gives access to the configuration values of a different module
@@ -217,7 +225,8 @@ public:
      * \param identifier The identifier of the config value
      * \return The configuration setting at the given identifier
      */
-    ElementPtr getRemoteConfigValue(const std::string& module_name, const std::string& identifier);
+    ConstElementPtr getRemoteConfigValue(const std::string& module_name,
+                                         const std::string& identifier) const;
     
 private:
     ModuleSpec readModuleSpecification(const std::string& filename);
@@ -226,13 +235,17 @@ private:
     std::string module_name_;
     isc::cc::AbstractSession& session_;
     ModuleSpec module_specification_;
-    ElementPtr handleConfigUpdate(ElementPtr new_config);
+    ConstElementPtr handleConfigUpdate(ConstElementPtr new_config);
 
-    isc::data::ElementPtr(*config_handler_)(isc::data::ElementPtr new_config);
-    isc::data::ElementPtr(*command_handler_)(const std::string& command, const isc::data::ElementPtr args);
+    isc::data::ConstElementPtr(*config_handler_)(
+        isc::data::ConstElementPtr new_config);
+    isc::data::ConstElementPtr(*command_handler_)(
+        const std::string& command,
+        isc::data::ConstElementPtr args);
 
     std::map<std::string, ConfigData> remote_module_configs_;
-    void updateRemoteConfig(const std::string& module_name, ElementPtr new_config);
+    void updateRemoteConfig(const std::string& module_name,
+                            ConstElementPtr new_config);
 };
 
 }

+ 28 - 26
src/lib/config/config_data.cc

@@ -36,15 +36,14 @@ namespace config {
 // If it is a map, we search through the list contained in its
 // 'map_item_spec' value. This code assumes the data has been
 // validated and conforms to the specification.
-static ElementPtr
-find_spec_part(ElementPtr spec, const std::string& identifier)
-{
+static ConstElementPtr
+find_spec_part(ConstElementPtr spec, const std::string& identifier) {
     //std::cout << "[XX] find_spec_part for " << identifier << std::endl;
     if (!spec) {
         isc_throw(DataNotFoundError, "Empty specification");
     }
     //std::cout << "in: " << std::endl << spec << std::endl;
-    ElementPtr spec_part = spec;
+    ConstElementPtr spec_part = spec;
     if (identifier == "") {
         isc_throw(DataNotFoundError, "Empty identifier");
     }
@@ -55,7 +54,7 @@ find_spec_part(ElementPtr spec, const std::string& identifier)
         //std::cout << "[XX] id part: " << part << std::endl;
         if (spec_part->getType() == Element::list) {
             bool found = false;
-            BOOST_FOREACH(ElementPtr list_el, spec_part->listValue()) {
+            BOOST_FOREACH(ConstElementPtr list_el, spec_part->listValue()) {
                 if (list_el->getType() == Element::map &&
                     list_el->contains("item_name") &&
                     list_el->get("item_name")->stringValue() == part) {
@@ -73,7 +72,7 @@ find_spec_part(ElementPtr spec, const std::string& identifier)
     if (id != "" && id != "/") {
         if (spec_part->getType() == Element::list) {
             bool found = false;
-            BOOST_FOREACH(ElementPtr list_el, spec_part->listValue()) {
+            BOOST_FOREACH(ConstElementPtr list_el, spec_part->listValue()) {
                 if (list_el->getType() == Element::map &&
                     list_el->contains("item_name") &&
                     list_el->get("item_name")->stringValue() == id) {
@@ -87,7 +86,8 @@ find_spec_part(ElementPtr spec, const std::string& identifier)
         } else if (spec_part->getType() == Element::map) {
             if (spec_part->contains("map_item_spec")) {
                 bool found = false;
-                BOOST_FOREACH(ElementPtr list_el, spec_part->get("map_item_spec")->listValue()) {
+                BOOST_FOREACH(ConstElementPtr list_el,
+                              spec_part->get("map_item_spec")->listValue()) {
                     if (list_el->getType() == Element::map &&
                         list_el->contains("item_name") &&
                         list_el->get("item_name")->stringValue() == id) {
@@ -113,10 +113,11 @@ find_spec_part(ElementPtr spec, const std::string& identifier)
 // Result must be a ListElement
 //
 static void
-spec_name_list(ElementPtr result, ElementPtr spec_part, std::string prefix, bool recurse = false)
+spec_name_list(ElementPtr result, ConstElementPtr spec_part,
+               const std::string& prefix, bool recurse = false)
 {
     if (spec_part->getType() == Element::list) {
-        BOOST_FOREACH(ElementPtr list_el, spec_part->listValue()) {
+        BOOST_FOREACH(ConstElementPtr list_el, spec_part->listValue()) {
             if (list_el->getType() == Element::map &&
                 list_el->contains("item_name")) {
                 std::string new_prefix = prefix;
@@ -136,26 +137,29 @@ spec_name_list(ElementPtr result, ElementPtr spec_part, std::string prefix, bool
                 }
             }
         }
-    } else if (spec_part->getType() == Element::map && spec_part->contains("map_item_spec")) {
-        spec_name_list(result, spec_part->get("map_item_spec"), prefix, recurse);
+    } else if (spec_part->getType() == Element::map &&
+               spec_part->contains("map_item_spec")) {
+        spec_name_list(result, spec_part->get("map_item_spec"), prefix,
+                       recurse);
     }
 }
 
-ElementPtr
-ConfigData::getValue(const std::string& identifier) {
+ConstElementPtr
+ConfigData::getValue(const std::string& identifier) const {
     // 'fake' is set, but dropped by this function and
     // serves no further purpose.
     bool fake;
     return (getValue(fake, identifier));
 }
 
-ElementPtr
-ConfigData::getValue(bool& is_default, const std::string& identifier) {
-    ElementPtr value = _config->find(identifier);
+ConstElementPtr
+ConfigData::getValue(bool& is_default, const std::string& identifier) const {
+    ConstElementPtr value = _config->find(identifier);
     if (value) {
         is_default = false;
     } else {
-        ElementPtr spec_part = find_spec_part(_module_spec.getConfigSpec(), identifier);
+        ConstElementPtr spec_part =
+            find_spec_part(_module_spec.getConfigSpec(), identifier);
         if (spec_part->contains("item_default")) {
             value = spec_part->get("item_default");
             is_default = true;
@@ -170,11 +174,10 @@ ConfigData::getValue(bool& is_default, const std::string& identifier) {
 /// Returns an ElementPtr pointing to a ListElement containing
 /// StringElements with the names of the options at the given
 /// identifier. If recurse is true, maps will be expanded as well
-ElementPtr
-ConfigData::getItemList(const std::string& identifier, bool recurse)
-{
+ConstElementPtr
+ConfigData::getItemList(const std::string& identifier, bool recurse) const {
     ElementPtr result = Element::createList();
-    ElementPtr spec_part = getModuleSpec().getConfigSpec();
+    ConstElementPtr spec_part = getModuleSpec().getConfigSpec();
     if (identifier != "" && identifier != "/") {
         spec_part = find_spec_part(spec_part, identifier);
     }
@@ -184,12 +187,11 @@ ConfigData::getItemList(const std::string& identifier, bool recurse)
 
 /// Returns an ElementPtr containing a MapElement with identifier->value
 /// pairs.
-ElementPtr
-ConfigData::getFullConfig()
-{
+ConstElementPtr
+ConfigData::getFullConfig() const {
     ElementPtr result = Element::createMap();
-    ElementPtr items = getItemList("", true);
-    BOOST_FOREACH(ElementPtr item, items->listValue()) {
+    ConstElementPtr items = getItemList("", true);
+    BOOST_FOREACH(ConstElementPtr item, items->listValue()) {
         result->set(item->stringValue(), getValue(item->stringValue()));
     }
     return (result);

+ 11 - 5
src/lib/config/config_data.h

@@ -55,7 +55,7 @@ public:
     /// Raises a DataNotFoundError if the identifier is bad.
     /// \param identifier The identifier pointing to the configuration
     ///        value that is to be returned
-    ElementPtr getValue(const std::string& identifier);
+    ConstElementPtr getValue(const std::string& identifier) const;
 
     /// Returns the value currently set for the given identifier
     /// If no value is set, the default value (as specified by the
@@ -67,10 +67,11 @@ public:
     ///                   false otherwise
     /// \param identifier The identifier pointing to the configuration
     ///        value that is to be returned
-    ElementPtr getValue(bool &is_default, const std::string& identifier);
+    ConstElementPtr getValue(bool& is_default,
+                             const std::string& identifier) const;
 
     /// Returns the ModuleSpec associated with this ConfigData object
-    const ModuleSpec getModuleSpec() { return (_module_spec); }
+    const ModuleSpec& getModuleSpec() const { return (_module_spec); }
 
     /// Set the ModuleSpec associated with this ConfigData object
     void setModuleSpec(ModuleSpec module_spec) { _module_spec = module_spec; };
@@ -96,14 +97,15 @@ public:
     ///         StringElements that specify the identifiers at the given
     ///         location (or all possible identifiers if identifier==""
     ///         and recurse==false)
-    ElementPtr getItemList(const std::string& identifier = "", bool recurse = false);
+    ConstElementPtr getItemList(const std::string& identifier = "",
+                                bool recurse = false) const;
 
     /// Returns all current configuration settings (both non-default and default).
     /// \return An ElementPtr pointing to a MapElement containing
     ///         string->value elements, where the string is the
     ///         full identifier of the configuration option and the
     ///         value is an ElementPtr with the value.
-    ElementPtr getFullConfig();
+    ConstElementPtr getFullConfig() const;
 
 private:
     ElementPtr _config;
@@ -113,3 +115,7 @@ private:
 }
 }
 #endif
+
+// Local Variables: 
+// mode: c++
+// End: 

+ 1 - 1
src/lib/config/documentation.txt

@@ -53,7 +53,7 @@ isc::data::ElementPtr my_config_handler(isc::data::ElementPtr new_config);
 
 The new_config is a ElementPtr pointing to a MapElement containing data in the form as specified by the specification file. It only contains values that were changed.
 
-The module can walk through this set and alter it's behaviour accordingly if necessary. It can also simply check them and return success (see below) and reference the needed configuration values directly when necessary by calling get_config_value(std::string identifier).
+The module can walk through this set and alter its behaviour accordingly if necessary. It can also simply check them and return success (see below) and reference the needed configuration values directly when necessary by calling get_config_value(std::string identifier).
 
 The callback function must return an answer message, which is created with isc::config::createAnswer(). For successful handling of the configuration, it should return the result of createAnswer(0) (0 being the result code for success). If there is a problem, the function can return the result of createAnswer(non-zero, "string_with_error_message"). In this case, the new configuration is not stored, and the error is fed back to the configuration manager.
 

+ 51 - 42
src/lib/config/module_spec.cc

@@ -24,15 +24,16 @@
 
 // todo: add more context to thrown ModuleSpecErrors?
 
-namespace isc {
-namespace config {
+using namespace isc::config;
 
+namespace {
 //
-// static functions
+// Private functions
 //
 
-static void
-check_leaf_item(const ElementPtr& spec, const std::string& name, Element::types type, bool mandatory)
+void
+check_leaf_item(ConstElementPtr spec, const std::string& name,
+                Element::types type, bool mandatory)
 {
     if (spec->contains(name)) {
         if (spec->get(name)->getType() == type) {
@@ -48,10 +49,10 @@ check_leaf_item(const ElementPtr& spec, const std::string& name, Element::types
     }
 }
 
-static void check_config_item_list(const ElementPtr& spec);
+void check_config_item_list(ConstElementPtr spec);
 
-static void
-check_config_item(const ElementPtr& spec) {
+void
+check_config_item(ConstElementPtr spec) {
     check_leaf_item(spec, "item_name", Element::string, true);
     check_leaf_item(spec, "item_type", Element::string, true);
     check_leaf_item(spec, "item_optional", Element::boolean, true);
@@ -72,35 +73,35 @@ check_config_item(const ElementPtr& spec) {
     }
 }
 
-static void
-check_config_item_list(const ElementPtr& spec) {
+void
+check_config_item_list(ConstElementPtr spec) {
     if (spec->getType() != Element::list) {
         throw ModuleSpecError("config_data is not a list of elements");
     }
-    BOOST_FOREACH(ElementPtr item, spec->listValue()) {
+    BOOST_FOREACH(ConstElementPtr item, spec->listValue()) {
         check_config_item(item);
     }
 }
 
-static void
-check_command(const ElementPtr& spec) {
+void
+check_command(ConstElementPtr spec) {
     check_leaf_item(spec, "command_name", Element::string, true);
     check_leaf_item(spec, "command_args", Element::list, true);
     check_config_item_list(spec->get("command_args"));
 }
 
-static void
-check_command_list(const ElementPtr& spec) {
+void
+check_command_list(ConstElementPtr spec) {
     if (spec->getType() != Element::list) {
         throw ModuleSpecError("commands is not a list of elements");
     }
-    BOOST_FOREACH(ElementPtr item, spec->listValue()) {
+    BOOST_FOREACH(ConstElementPtr item, spec->listValue()) {
         check_command(item);
     }
 }
 
-static void
-check_data_specification(const ElementPtr& spec) {
+void
+check_data_specification(ConstElementPtr spec) {
     check_leaf_item(spec, "module_name", Element::string, true);
     check_leaf_item(spec, "module_description", Element::string, false);
     // config_data is not mandatory; module could just define
@@ -115,21 +116,23 @@ check_data_specification(const ElementPtr& spec) {
 
 // checks whether the given element is a valid module specification
 // throws a ModuleSpecError if the specification is bad
-static void
-check_module_specification(const ElementPtr& def)
-{
+void
+check_module_specification(ConstElementPtr def) {
     try {
         check_data_specification(def);
     } catch (TypeError te) {
         throw ModuleSpecError(te.what());
     }
 }
+}
 
+namespace isc {
+namespace config {
 //
 // Public functions
 //
 
-ModuleSpec::ModuleSpec(ElementPtr module_spec_element,
+ModuleSpec::ModuleSpec(ConstElementPtr module_spec_element,
                        const bool check)
                        throw(ModuleSpecError)
                        
@@ -140,7 +143,7 @@ ModuleSpec::ModuleSpec(ElementPtr module_spec_element,
     }
 }
 
-const ElementPtr
+ConstElementPtr
 ModuleSpec::getCommandsSpec() const {
     if (module_specification->contains("commands")) {
         return (module_specification->get("commands"));
@@ -149,7 +152,7 @@ ModuleSpec::getCommandsSpec() const {
     }
 }
 
-const ElementPtr
+ConstElementPtr
 ModuleSpec::getConfigSpec() const {
     if (module_specification->contains("config_data")) {
         return (module_specification->get("config_data"));
@@ -173,16 +176,16 @@ ModuleSpec::getModuleDescription() const {
 }
 
 bool
-ModuleSpec::validate_config(const ElementPtr data, const bool full) {
-    ElementPtr spec = module_specification->find("config_data");
+ModuleSpec::validate_config(ConstElementPtr data, const bool full) const {
+    ConstElementPtr spec = module_specification->find("config_data");
     return (validate_spec_list(spec, data, full, ElementPtr()));
 }
 
 bool
-ModuleSpec::validate_config(const ElementPtr data, const bool full,
-                            ElementPtr errors)
+ModuleSpec::validate_config(ConstElementPtr data, const bool full,
+                            ElementPtr errors) const
 {
-    ElementPtr spec = module_specification->find("config_data");
+    ConstElementPtr spec = module_specification->find("config_data");
     return (validate_spec_list(spec, data, full, errors));
 }
 
@@ -199,7 +202,7 @@ moduleSpecFromFile(const std::string& file_name, const bool check)
         throw ModuleSpecError(errs.str());
     }
 
-    ElementPtr module_spec_element = Element::fromJSON(file, file_name);
+    ConstElementPtr module_spec_element = Element::fromJSON(file, file_name);
     if (module_spec_element->contains("module_spec")) {
         return (ModuleSpec(module_spec_element->get("module_spec"), check));
     } else {
@@ -211,7 +214,7 @@ ModuleSpec
 moduleSpecFromFile(std::ifstream& in, const bool check)
                    throw(JSONError, ModuleSpecError)
 {
-    ElementPtr module_spec_element = Element::fromJSON(in);
+    ConstElementPtr module_spec_element = Element::fromJSON(in);
     if (module_spec_element->contains("module_spec")) {
         return (ModuleSpec(module_spec_element->get("module_spec"), check));
     } else {
@@ -220,6 +223,7 @@ moduleSpecFromFile(std::ifstream& in, const bool check)
 }
 
 
+namespace {
 //
 // private functions
 //
@@ -227,9 +231,8 @@ moduleSpecFromFile(std::ifstream& in, const bool check)
 //
 // helper functions for validation
 //
-static bool
-check_type(ElementPtr spec, ElementPtr element)
-{
+bool
+check_type(ConstElementPtr spec, ConstElementPtr element) {
     std::string cur_item_type;
     cur_item_type = spec->get("item_type")->stringValue();
     if (cur_item_type == "any") {
@@ -257,9 +260,12 @@ check_type(ElementPtr spec, ElementPtr element)
     }
     return (false);
 }
+}
 
 bool
-ModuleSpec::validate_item(const ElementPtr spec, const ElementPtr data, const bool full, ElementPtr errors) {
+ModuleSpec::validate_item(ConstElementPtr spec, ConstElementPtr data,
+                          const bool full, ElementPtr errors) const
+{
     if (!check_type(spec, data)) {
         // we should do some proper error feedback here
         // std::cout << "type mismatch; not " << spec->get("item_type") << ": " << data << std::endl;
@@ -270,8 +276,8 @@ ModuleSpec::validate_item(const ElementPtr spec, const ElementPtr data, const bo
         return (false);
     }
     if (data->getType() == Element::list) {
-        ElementPtr list_spec = spec->get("list_item_spec");
-        BOOST_FOREACH(ElementPtr list_el, data->listValue()) {
+        ConstElementPtr list_spec = spec->get("list_item_spec");
+        BOOST_FOREACH(ConstElementPtr list_el, data->listValue()) {
             if (!check_type(list_spec, list_el)) {
                 if (errors) {
                     errors->add(Element::create("Type mismatch"));
@@ -295,10 +301,12 @@ ModuleSpec::validate_item(const ElementPtr spec, const ElementPtr data, const bo
 
 // spec is a map with item_name etc, data is a map
 bool
-ModuleSpec::validate_spec(const ElementPtr spec, const ElementPtr data, const bool full, ElementPtr errors) {
+ModuleSpec::validate_spec(ConstElementPtr spec, ConstElementPtr data,
+                          const bool full, ElementPtr errors) const
+{
     std::string item_name = spec->get("item_name")->stringValue();
     bool optional = spec->get("item_optional")->boolValue();
-    ElementPtr data_el;
+    ConstElementPtr data_el;
     data_el = data->get(item_name);
     
     if (data_el) {
@@ -318,10 +326,11 @@ ModuleSpec::validate_spec(const ElementPtr spec, const ElementPtr data, const bo
 
 // spec is a list of maps, data is a map
 bool
-ModuleSpec::validate_spec_list(const ElementPtr spec, const ElementPtr data, const bool full, ElementPtr errors) {
-    ElementPtr cur_data_el;
+ModuleSpec::validate_spec_list(ConstElementPtr spec, ConstElementPtr data,
+                               const bool full, ElementPtr errors) const
+{
     std::string cur_item_name;
-    BOOST_FOREACH(ElementPtr cur_spec_el, spec->listValue()) {
+    BOOST_FOREACH(ConstElementPtr cur_spec_el, spec->listValue()) {
         if (!validate_spec(cur_spec_el, data, full, errors)) {
             return (false);
         }

+ 22 - 13
src/lib/config/module_spec.h

@@ -51,28 +51,28 @@ namespace isc { namespace config {
     ///
     class ModuleSpec {
     public:
-        explicit ModuleSpec() {};
+        ModuleSpec() {};
         /// Create a \c ModuleSpec instance with the given data as
         /// the specification
         /// \param e The Element containing the data specification
-        explicit ModuleSpec(ElementPtr e, const bool check = true)
-                            throw(ModuleSpecError);
+        explicit ModuleSpec(ConstElementPtr e, const bool check = true)
+            throw(ModuleSpecError);
 
         /// Returns the commands part of the specification as an
         /// ElementPtr, returns an empty ElementPtr if there is none
         /// \return ElementPtr Shared pointer to the commands
         ///                    part of the specification
-        const ElementPtr getCommandsSpec() const;
+        ConstElementPtr getCommandsSpec() const;
 
         /// Returns the configuration part of the specification as an
         /// ElementPtr
         /// \return ElementPtr Shared pointer to the configuration
         ///                    part of the specification
-        const ElementPtr getConfigSpec() const;
+        ConstElementPtr getConfigSpec() const;
 
         /// Returns the full module specification as an ElementPtr
         /// \return ElementPtr Shared pointer to the specification
-        const ElementPtr getFullSpec() const { return (module_specification); }
+        ConstElementPtr getFullSpec() const { return module_specification; }
 
         /// Returns the module name as specified by the specification
         const std::string getModuleName() const;
@@ -87,17 +87,22 @@ namespace isc { namespace config {
         /// \param data The base \c Element of the data to check
         /// \return true if the data conforms to the specification,
         /// false otherwise.
-        bool validate_config(const ElementPtr data, const bool full = false);
+        bool validate_config(ConstElementPtr data,
+                             const bool full = false) const;
 
         /// errors must be of type ListElement
-        bool validate_config(const ElementPtr data, const bool full, ElementPtr errors);
+        bool validate_config(ConstElementPtr data, const bool full,
+                             ElementPtr errors) const;
 
     private:
-        bool validate_item(const ElementPtr spec, const ElementPtr data, const bool full, ElementPtr errors);
-        bool validate_spec(const ElementPtr spec, const ElementPtr data, const bool full, ElementPtr errors);
-        bool validate_spec_list(const ElementPtr spec, const ElementPtr data, const bool full, ElementPtr errors);
-
-        ElementPtr module_specification;
+        bool validate_item(ConstElementPtr spec, ConstElementPtr data,
+                           const bool full, ElementPtr errors) const;
+        bool validate_spec(ConstElementPtr spec, ConstElementPtr data,
+                           const bool full, ElementPtr errors) const;
+        bool validate_spec_list(ConstElementPtr spec, ConstElementPtr data,
+                                const bool full, ElementPtr errors) const;
+
+        ConstElementPtr module_specification;
     };
 
     /// Creates a \c ModuleSpec instance from the contents
@@ -126,3 +131,7 @@ namespace isc { namespace config {
 } }
 
 #endif // _DATA_DEF_H
+
+// Local Variables: 
+// mode: c++
+// End: 

+ 3 - 5
src/lib/config/tests/Makefile.am

@@ -21,15 +21,13 @@ TESTS += run_unittests
 run_unittests_SOURCES = ccsession_unittests.cc module_spec_unittests.cc config_data_unittests.cc run_unittests.cc
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+# TODO: remove PTHREAD_LDFLAGS (and from configure too)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) $(PTHREAD_LDFLAGS)
 run_unittests_LDADD =  $(GTEST_LDADD)
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
 run_unittests_LDADD += libfake_session.la
 run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
-# link *only* to data.o from lib/cc (more importantly, don't link in
-# the session class provided there, since we use our own fake_session
-# here)
-run_unittests_LDADD += $(top_builddir)/src/lib/cc/data.o
 
 endif
 

File diff suppressed because it is too large
+ 16 - 16
src/lib/config/tests/ccsession_unittests.cc


+ 25 - 46
src/lib/config/tests/fake_session.cc

@@ -40,12 +40,12 @@ using namespace isc::data;
 
 // ok i want these in cc/data 
 bool
-listContains(ElementPtr list, ElementPtr el) {
+listContains(ConstElementPtr list, ConstElementPtr el) {
     if (!list) {
         return (false);
     }
-    BOOST_FOREACH(ElementPtr l_el, list->listValue()) {
-        if (l_el == el) {
+    BOOST_FOREACH(ConstElementPtr l_el, list->listValue()) {
+        if (*l_el == *el) {
             return (true);
         }
     }
@@ -53,10 +53,10 @@ listContains(ElementPtr list, ElementPtr el) {
 }
 
 void
-listRemove(ElementPtr list, ElementPtr el) {
+listRemove(ElementPtr list, ConstElementPtr el) {
     int i = -1;
-    BOOST_FOREACH(ElementPtr s_el, list->listValue()) {
-        if (el == s_el) {
+    BOOST_FOREACH(ConstElementPtr s_el, list->listValue()) {
+        if (*el == *s_el) {
             i = 0;
         }
         i++;
@@ -82,11 +82,6 @@ FakeSession::FakeSession(isc::data::ElementPtr initial_messages,
 FakeSession::~FakeSession() {
 }
 
-bool
-FakeSession::connect() {
-    return (true);
-}
-
 void
 FakeSession::disconnect() {
 }
@@ -99,25 +94,8 @@ void
 FakeSession::establish(const char* socket_file) {
 }
 
-//
-// Convert to wire format and send this on the TCP stream with its length prefix
-//
-void
-FakeSession::sendmsg(ElementPtr& msg) {
-    //cout << "[XX] client sends message: " << msg << endl;
-    // err, to where?
-    addMessage(msg, "*", "*");
-}
-
-void
-FakeSession::sendmsg(ElementPtr& env, ElementPtr& msg) {
-    //cout << "[XX] client sends message: " << msg << endl;
-    //cout << "[XX] env: " << env << endl;
-    addMessage(msg, env->get("group")->stringValue(), env->get("to")->stringValue());
-}
-
 bool
-FakeSession::recvmsg(ElementPtr& msg, bool nonblock UNUSED_PARAM,
+FakeSession::recvmsg(ConstElementPtr& msg, bool nonblock UNUSED_PARAM,
                      int seq UNUSED_PARAM)
 {
     //cout << "[XX] client asks for message " << endl;
@@ -133,7 +111,7 @@ FakeSession::recvmsg(ElementPtr& msg, bool nonblock UNUSED_PARAM,
 }
 
 bool
-FakeSession::recvmsg(ElementPtr& env, ElementPtr& msg,
+FakeSession::recvmsg(ConstElementPtr& env, ConstElementPtr& msg,
                      bool nonblock UNUSED_PARAM,
                      int seq UNUSED_PARAM)
 {
@@ -147,12 +125,13 @@ FakeSession::recvmsg(ElementPtr& env, ElementPtr& msg,
         messages_->remove(0);
         return (true);
     } else if (msg_queue_) {
-        BOOST_FOREACH(ElementPtr c_m, msg_queue_->listValue()) {
-            ElementPtr to_remove = ElementPtr();
+        BOOST_FOREACH(ConstElementPtr c_m, msg_queue_->listValue()) {
+            ConstElementPtr to_remove = ElementPtr();
             if (haveSubscription(c_m->get(0), c_m->get(1))) {
-                env = Element::createMap();
-                env->set("group", c_m->get(0));
-                env->set("to", c_m->get(1));
+                ElementPtr new_env = Element::createMap();
+                new_env->set("group", c_m->get(0));
+                new_env->set("to", c_m->get(1));
+                env = new_env;
                 msg = c_m->get(2);
                 to_remove = c_m;
             }
@@ -192,7 +171,7 @@ FakeSession::unsubscribe(std::string group, std::string instance) {
 }
 
 int
-FakeSession::group_sendmsg(ElementPtr msg, std::string group,
+FakeSession::group_sendmsg(ConstElementPtr msg, std::string group,
                            std::string to, std::string instance UNUSED_PARAM)
 {
     //cout << "[XX] client sends message: " << msg << endl;
@@ -202,28 +181,29 @@ FakeSession::group_sendmsg(ElementPtr msg, std::string group,
 }
 
 bool
-FakeSession::group_recvmsg(ElementPtr& envelope, ElementPtr& msg,
+FakeSession::group_recvmsg(ConstElementPtr& envelope, ConstElementPtr& msg,
                            bool nonblock, int seq)
 {
     return (recvmsg(envelope, msg, nonblock, seq));
 }
 
 int
-FakeSession::reply(ElementPtr& envelope, ElementPtr& newmsg) {
+FakeSession::reply(ConstElementPtr envelope, ConstElementPtr newmsg) {
     //cout << "[XX] client sends reply: " << newmsg << endl;
     //cout << "[XX] env: " << envelope << endl;
-    addMessage(newmsg, envelope->get("group")->stringValue(), envelope->get("to")->stringValue());
+    addMessage(newmsg, envelope->get("group")->stringValue(),
+               envelope->get("to")->stringValue());
     return (1);
 }
 
 bool
-FakeSession::hasQueuedMsgs() {
+FakeSession::hasQueuedMsgs() const {
     return (false);
 }
 
-ElementPtr
-FakeSession::getFirstMessage(std::string& group, std::string& to) {
-    ElementPtr el;
+ConstElementPtr
+FakeSession::getFirstMessage(std::string& group, std::string& to) const {
+    ConstElementPtr el;
     if (msg_queue_ && msg_queue_->size() > 0) {
         el = msg_queue_->get(0);
         msg_queue_->remove(0);
@@ -238,7 +218,7 @@ FakeSession::getFirstMessage(std::string& group, std::string& to) {
 }
 
 void
-FakeSession::addMessage(ElementPtr msg, const std::string& group,
+FakeSession::addMessage(ConstElementPtr msg, const std::string& group,
                         const std::string& to)
 {
     ElementPtr m_el = Element::createList();
@@ -270,8 +250,7 @@ FakeSession::haveSubscription(const std::string& group,
 }
 
 bool
-FakeSession::haveSubscription(const ElementPtr group,
-                              const ElementPtr instance)
+FakeSession::haveSubscription(ConstElementPtr group, ConstElementPtr instance)
 {
     return (haveSubscription(group->stringValue(), instance->stringValue()));
 }

+ 19 - 21
src/lib/config/tests/fake_session.h

@@ -47,40 +47,32 @@ public:
     virtual void startRead(boost::function<void()> read_callback);
 
     virtual void establish(const char* socket_file = NULL);
-    bool connect();
     virtual void disconnect();
-    void sendmsg(isc::data::ElementPtr& msg);
-    void sendmsg(isc::data::ElementPtr& env,
-                 isc::data::ElementPtr& msg);
-    bool recvmsg(isc::data::ElementPtr& msg,
-                 bool nonblock = true, int seq = -1);
-    bool recvmsg(isc::data::ElementPtr& env,
-                 isc::data::ElementPtr& msg,
-                 bool nonblock = true, int seq = -1);
     virtual void subscribe(std::string group,
                            std::string instance = "*");
     virtual void unsubscribe(std::string group,
                              std::string instance = "*");
-    virtual int group_sendmsg(isc::data::ElementPtr msg,
+    virtual int group_sendmsg(isc::data::ConstElementPtr msg,
                               std::string group,
                               std::string instance = "*",
                               std::string to = "*");
-    virtual bool group_recvmsg(isc::data::ElementPtr& envelope,
-                               isc::data::ElementPtr& msg,
+    virtual bool group_recvmsg(isc::data::ConstElementPtr& envelope,
+                               isc::data::ConstElementPtr& msg,
                                bool nonblock = true,
                                int seq = -1);
-    virtual int reply(isc::data::ElementPtr& envelope,
-                      isc::data::ElementPtr& newmsg);
-    virtual bool hasQueuedMsgs();
-    virtual void setTimeout(size_t milliseconds) {};
-    virtual size_t getTimeout() const { return 0; };
-    isc::data::ElementPtr getFirstMessage(std::string& group, std::string& to);
-    void addMessage(isc::data::ElementPtr, const std::string& group,
+    virtual int reply(isc::data::ConstElementPtr envelope,
+                      isc::data::ConstElementPtr newmsg);
+    virtual bool hasQueuedMsgs() const;
+    virtual void setTimeout(size_t milliseconds) {}
+    virtual size_t getTimeout() const { return (0); }
+    isc::data::ConstElementPtr getFirstMessage(std::string& group,
+                                               std::string& to) const;
+    void addMessage(isc::data::ConstElementPtr, const std::string& group,
                     const std::string& to);
     bool haveSubscription(const std::string& group,
                           const std::string& instance);
-    bool haveSubscription(const isc::data::ElementPtr group,
-                          const isc::data::ElementPtr instance);
+    bool haveSubscription(const isc::data::ConstElementPtr group,
+                          const isc::data::ConstElementPtr instance);
 
     // For the convenience of tests, we share these internal members
     // with the tester.  The test code may insert update and check,
@@ -90,6 +82,12 @@ public:
     isc::data::ElementPtr getMsgQueue() { return (msg_queue_); }
 
 private:
+    bool recvmsg(isc::data::ConstElementPtr& msg,
+                 bool nonblock = true, int seq = -1);
+    bool recvmsg(isc::data::ConstElementPtr& env,
+                 isc::data::ConstElementPtr& msg,
+                 bool nonblock = true, int seq = -1);
+
     const isc::data::ElementPtr messages_;
     isc::data::ElementPtr subscriptions_;
     isc::data::ElementPtr msg_queue_;

+ 5 - 4
src/lib/config/tests/module_spec_unittests.cc

@@ -134,23 +134,24 @@ TEST(ModuleSpec, SpecfileCommands)
 }
 
 bool
-data_test(ModuleSpec dd, const std::string& data_file_name) {
+data_test(const ModuleSpec& dd, const std::string& data_file_name) {
     std::ifstream data_file;
 
     data_file.open(specfile(data_file_name).c_str());
-    ElementPtr data = Element::fromJSON(data_file, data_file_name);
+    ConstElementPtr data = Element::fromJSON(data_file, data_file_name);
     data_file.close();
 
     return (dd.validate_config(data));
 }
 
 bool
-data_test_with_errors(ModuleSpec dd, const std::string& data_file_name, ElementPtr errors)
+data_test_with_errors(const ModuleSpec& dd, const std::string& data_file_name,
+                      ElementPtr errors)
 {
     std::ifstream data_file;
 
     data_file.open(specfile(data_file_name).c_str());
-    ElementPtr data = Element::fromJSON(data_file, data_file_name);
+    ConstElementPtr data = Element::fromJSON(data_file, data_file_name);
     data_file.close();
 
     return (dd.validate_config(data, true, errors));

+ 6 - 1
src/lib/datasrc/data_source.cc

@@ -206,6 +206,11 @@ checkCache(QueryTask& task, RRsetList& target) {
         if (!hit || !rrset || (flags & DataSrc::CNAME_FOUND) != 0) {
             hit = cache.retrieve(task.qname, task.qclass, RRType::CNAME(),
                                  rrset, flags);
+            if (!rrset) {
+                // If we don't have a positive cache, forget it; otherwise the
+                // intermediate result may confuse the subsequent processing.
+                hit = false;
+            }
         }
 
         if (hit) {
@@ -1245,7 +1250,7 @@ Nsec3Param::getHash(const Name& name) const {
 // installed files we define the methods here.
 //
 DataSrc::Result
-DataSrc::init(const isc::data::ElementPtr config UNUSED_PARAM) {
+DataSrc::init(isc::data::ConstElementPtr config UNUSED_PARAM) {
     return (NOT_IMPLEMENTED);
 }
 

+ 2 - 2
src/lib/datasrc/data_source.h

@@ -115,7 +115,7 @@ public:
     // Optional 'low-level' methods.  These will have stub implementations
     // in the general DataSrc class but MAY be overwritten by subclasses
     virtual Result init() = 0;
-    virtual Result init(const isc::data::ElementPtr config) = 0;
+    virtual Result init(isc::data::ConstElementPtr config) = 0;
     virtual Result close() = 0;
 
     // Mandatory 'low-level' methods: These will NOT be implemented by
@@ -187,7 +187,7 @@ public:
     void setClass(const isc::dns::RRClass& c) { rrclass = c; }
 
     Result init() { return (NOT_IMPLEMENTED); }
-    Result init(const isc::data::ElementPtr config);
+    Result init(isc::data::ConstElementPtr config);
     Result close() { return (NOT_IMPLEMENTED); }
 
     virtual Result findRRset(const isc::dns::Name& qname,

+ 1 - 1
src/lib/datasrc/sqlite3_datasrc.cc

@@ -558,7 +558,7 @@ Sqlite3DataSrc::~Sqlite3DataSrc() {
 }
 
 DataSrc::Result
-Sqlite3DataSrc::init(const isc::data::ElementPtr config) {
+Sqlite3DataSrc::init(isc::data::ConstElementPtr config) {
     if (config && config->contains("database_file")) {
         open(config->get("database_file")->stringValue());
     } else {

+ 1 - 1
src/lib/datasrc/sqlite3_datasrc.h

@@ -95,7 +95,7 @@ public:
                              isc::dns::RRsetList& target) const;
 
     Result init() { return (init(isc::data::ElementPtr())); }
-    Result init(const isc::data::ElementPtr config);
+    Result init(const isc::data::ConstElementPtr config);
     Result close();
 
 private:

+ 1 - 1
src/lib/datasrc/static_datasrc.cc

@@ -260,7 +260,7 @@ StaticDataSrc::init() {
 // Static data source is "configuration less", so the \c config parameter
 // is intentionally ignored.
 DataSrc::Result
-StaticDataSrc::init(const isc::data::ElementPtr config UNUSED_PARAM) {
+StaticDataSrc::init(isc::data::ConstElementPtr config UNUSED_PARAM) {
     return (init());
 }
 

+ 1 - 1
src/lib/datasrc/static_datasrc.h

@@ -81,7 +81,7 @@ public:
                             isc::dns::RRsetList& target) const;
 
     Result init();
-    Result init(const isc::data::ElementPtr config);
+    Result init(isc::data::ConstElementPtr config);
     Result close();
 private:
     StaticDataSrcImpl* impl_;

+ 19 - 1
src/lib/datasrc/tests/datasrc_unittest.cc

@@ -49,7 +49,7 @@ using namespace isc::datasrc;
 using namespace isc::data;
 
 namespace {
-const ElementPtr SQLITE_DBFILE_EXAMPLE = Element::fromJSON(
+ConstElementPtr SQLITE_DBFILE_EXAMPLE = Element::fromJSON(
     "{ \"database_file\": \"" TEST_DATA_DIR "/example.org.sqlite3\"}");
 
 class DataSrcTest : public ::testing::Test {
@@ -915,6 +915,24 @@ TEST_F(DataSrcTest, RootDSQuery) {
     headerCheck(msg, Rcode::REFUSED(), true, false, true, 0, 0, 0);
 }
 
+TEST_F(DataSrcTest, DSQueryFromCache) {
+    // explicitly enable hot spot cache
+    cache.setEnabled(true);
+
+    // The first query will create a negative cache for example.org/CNAME
+    createAndProcessQuery(Name("example.org"), RRClass::IN(), RRType::SOA());
+
+    // the cached CNAME shouldn't confuse subsequent query.
+    // there may be several different possible cases that could trigger a bug,
+    // but DS query is the only known example.
+    msg.clear(Message::PARSE);
+    createAndProcessQuery(Name("example.org"), RRClass::IN(), RRType::DS());
+
+    // returning refused is probably a bad behavior, but it's a different
+    // issue -- see Trac Ticket #306.
+    headerCheck(msg, Rcode::REFUSED(), true, false, true, 0, 0, 0);
+}
+
 // Non-existent name in the "static" data source.  The purpose of this test
 // is to check a corner case behavior when atypical RRClass (CH in this case)
 // is specified.

+ 6 - 6
src/lib/datasrc/tests/sqlite3_unittest.cc

@@ -43,22 +43,22 @@ using namespace isc::datasrc;
 using namespace isc::data;
 
 namespace {
-ElementPtr SQLITE_DBFILE_EXAMPLE = Element::fromJSON(
+ConstElementPtr SQLITE_DBFILE_EXAMPLE = Element::fromJSON(
     "{ \"database_file\": \"" TEST_DATA_DIR "/test.sqlite3\"}");
-ElementPtr SQLITE_DBFILE_EXAMPLE2 = Element::fromJSON(
+ConstElementPtr SQLITE_DBFILE_EXAMPLE2 = Element::fromJSON(
     "{ \"database_file\": \"" TEST_DATA_DIR "/example2.com.sqlite3\"}");
-ElementPtr SQLITE_DBFILE_EXAMPLE_ROOT = Element::fromJSON(
+ConstElementPtr SQLITE_DBFILE_EXAMPLE_ROOT = Element::fromJSON(
     "{ \"database_file\": \"" TEST_DATA_DIR "/test-root.sqlite3\"}");
-ElementPtr SQLITE_DBFILE_BROKENDB = Element::fromJSON(
+ConstElementPtr SQLITE_DBFILE_BROKENDB = Element::fromJSON(
     "{ \"database_file\": \"" TEST_DATA_DIR "/brokendb.sqlite3\"}");
-ElementPtr SQLITE_DBFILE_MEMORY = Element::fromJSON(
+ConstElementPtr SQLITE_DBFILE_MEMORY = Element::fromJSON(
     "{ \"database_file\": \":memory:\"}");
 
 // The following file must be non existent and must be non"creatable";
 // the sqlite3 library will try to create a new DB file if it doesn't exist,
 // so to test a failure case the create operation should also fail.
 // The "nodir", a non existent directory, is inserted for this purpose.
-ElementPtr SQLITE_DBFILE_NOTEXIST = Element::fromJSON(
+ConstElementPtr SQLITE_DBFILE_NOTEXIST = Element::fromJSON(
     "{ \"database_file\": \"" TEST_DATA_DIR "/nodir/notexist\"}");
 
 const string sigdata_common(" 20100322084538 20100220084538 "

+ 1 - 2
src/lib/datasrc/tests/test_datasrc.cc

@@ -307,8 +307,7 @@ vector<Zone> zones;
 }
 
 DataSrc::Result
-TestDataSrc::init(const isc::data::ElementPtr config UNUSED_PARAM)
-{
+TestDataSrc::init(isc::data::ConstElementPtr config UNUSED_PARAM) {
     return (init());
 }
 

+ 1 - 1
src/lib/datasrc/tests/test_datasrc.h

@@ -85,7 +85,7 @@ public:
                              isc::dns::RRsetList& target) const;
 
     Result init();
-    Result init(const isc::data::ElementPtr config);
+    Result init(isc::data::ConstElementPtr config);
     Result close() { return (SUCCESS); }
 
 private:

+ 2 - 2
src/lib/dns/Makefile.am

@@ -89,8 +89,8 @@ rrparamregistry.cc: rrparamregistry-placeholder.cc
 rrclass.h rrtype.h rrparamregistry.cc rdataclass.h rdataclass.cc: Makefile
 	./gen-rdatacode.py
 
-libdns++_includedir = $(includedir)/dns
-libdns++_include_HEADERS = \
+libdns___includedir = $(includedir)/dns
+libdns___include_HEADERS = \
 	buffer.h \
 	dnssectime.h \
 	exceptions.h \

+ 0 - 71
src/lib/dns/message_test.py

@@ -1,71 +0,0 @@
-from bind10_dns import *
-from struct import *
-
-id = ["10","35"]
-flags = ["85", "00"]
-sections = ["00","01","00","02","00","00","00","00"]
-query_raw_name = ["04","74","65","73","74","07","65","78","61","6d","70","6c","65","03","63","6f","6d","00"]
-query_type_class = ["00","01","00","01"]
-answer_compress = ["c0","0c"]
-answer1_ttl_type_class_rdlen_rdata = ["00","01","00","01","00","00","0e","10","00","04","c0","00","02","01"]
-answer2_ttl_type_class_rdlen_rdata = ["00","01","00","01","00","00","1c","20","00","04","c0","00","02","02"]
-
-bytes = pack("B" * len(query_raw_name), *[int(i,16) for i in query_raw_name])
-query_name = name(input_buffer(bytes))
-print("query name is ", query_name.to_text())
-
-message_raw_data = id + flags + sections + query_raw_name + query_type_class + answer_compress + answer1_ttl_type_class_rdlen_rdata + answer_compress + \
-        answer2_ttl_type_class_rdlen_rdata
-
-m = message(message_mode.PARSE)
-message_bytes = pack("B" * len(message_raw_data), *[int(i,16) for i in message_raw_data])
-m.from_wire(input_buffer(message_bytes))
-if m.get_qid() == int("1035", 16):
-    print("id is correct")
-
-if m.get_opcode() == op_code.QUERY():
-    print("opcode is correct")
-
-if m.get_rcode() == rcode.NOERROR():
-    print("rcode is correct")
-
-if m.get_header_flag(message_flag.QR()):
-    print("qr is correct")
-
-if m.get_header_flag(message_flag.RD()):
-    print("rd is correct")
-
-if m.get_header_flag(message_flag.AA()):
-    print("aa is correct")
-
-if m.get_rr_count(section.QUESTION()) == 1:
-    print("qustion rr count is correct")
-
-if m.get_rr_count(section.ANSWER()) == 2:
-    print("answer rr count is ok")
-
-if m.get_rr_count(section.AUTHORITY()) == 0 and m.get_rr_count(section.ADDITIONAL()) == 0:
-    print("authority and additional rr count is ok")
-
-question_iter = question_iter(m)
-question = question_iter.get_question()
-print("question name is ", question.get_name().to_text())
-if question.get_type() == rr_type.A():
-    print("question rr type is A")
-if question.get_class() == rr_class.IN():
-    print("question rr class is IN")
-
-answer_rrset_iter = section_iter(m, section.ANSWER())
-answer = answer_rrset_iter.get_rrset()
-print("answer part name is ", answer.get_name().to_text())
-if answer.get_type() == rr_type.A() and answer.get_class() == rr_class.IN():
-    print("answer part is A record and class IN")
-
-rdata_iter = answer.get_rdata_iterator()
-rdata_iter.first()
-print("first part of rdata is ", rdata_iter.get_current().to_text())
-rdata_iter.next()
-print("second part of rdata is", rdata_iter.get_current().to_text())
-rdata_iter.next()
-if rdata_iter.is_last():
-    print("answer part has two rr")

+ 1 - 1
src/lib/dns/messagerenderer.h

@@ -109,7 +109,7 @@ public:
     /// The destructor does nothing on the given \c buffer on construction;
     /// in fact, it is expected that the user will use the resulting buffer
     /// for some post rendering purposes (e.g., send the data to the network).
-    /// It's user's responsibility to do any necessary cleanup for the
+    /// It's the user's responsibility to do any necessary cleanup for the
     /// \c buffer.
     ~MessageRenderer();
     //@}

+ 7 - 8
src/lib/dns/name.cc

@@ -601,17 +601,17 @@ Name::isWildcard() const {
 
 Name
 Name::concatenate(const Name& suffix) const {
-    assert(this->length_ > 0 && suffix.length_ > 0);
-    assert(this->labelcount_ > 0 && suffix.labelcount_ > 0);
+    assert(length_ > 0 && suffix.length_ > 0);
+    assert(labelcount_ > 0 && suffix.labelcount_ > 0);
 
-    unsigned int length = this->length_ + suffix.length_ - 1;
+    unsigned int length = length_ + suffix.length_ - 1;
     if (length > Name::MAX_WIRE) {
         isc_throw(TooLongName, "names are too long to concatenate");
     }
 
     Name retname;
     retname.ndata_.reserve(length);
-    retname.ndata_.assign(this->ndata_, 0, this->length_ - 1);
+    retname.ndata_.assign(ndata_, 0, length_ - 1);
     retname.ndata_.insert(retname.ndata_.end(),
                           suffix.ndata_.begin(), suffix.ndata_.end());
     assert(retname.ndata_.size() == length);
@@ -622,14 +622,13 @@ Name::concatenate(const Name& suffix) const {
     // excluding that for the trailing dot, and append the offsets of the
     // suffix name with the additional offset of the length of the prefix.
     //
-    unsigned int labels = this->labelcount_ + suffix.labelcount_ - 1;
+    unsigned int labels = labelcount_ + suffix.labelcount_ - 1;
     assert(labels <= Name::MAX_LABELS);
     retname.offsets_.reserve(labels);
-    retname.offsets_.assign(&this->offsets_[0],
-                            &this->offsets_[0] + this->labelcount_ - 1);
+    retname.offsets_.assign(&offsets_[0], &offsets_[0] + labelcount_ - 1);
     transform(suffix.offsets_.begin(), suffix.offsets_.end(),
               back_inserter(retname.offsets_),
-              bind2nd(plus<char>(), this->length_ - 1));
+              bind2nd(plus<char>(), length_ - 1));
     assert(retname.offsets_.size() == labels);
     retname.labelcount_ = labels;
 

+ 10 - 21
src/lib/dns/python/Makefile.am

@@ -1,27 +1,16 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
-
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
-if USE_GXX
-AM_CXXFLAGS += -Wno-write-strings
-endif
-
-#lib_LTLIBRARIES = libdns_python_name.la libdns_python_rrset.la
-#libdns_python_name_la_SOURCES = name_python.cc
-#libdns_python_name_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
-#libdns_python_name_la_LDFLAGS = $(PYTHON_LDFLAGS)
-
-#lib_LTLIBRARIES = libdns_python_name.la libdns_python_rrset.la
-pyexec_LTLIBRARIES = libdns_python.la
-libdns_python_la_SOURCES = libdns_python.cc libdns_python_common.cc
-libdns_python_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
-libdns_python_la_LDFLAGS = $(PYTHON_LDFLAGS)
+pyexec_LTLIBRARIES = pydnspp.la
+pydnspp_la_SOURCES = pydnspp.cc pydnspp_common.cc
+pydnspp_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
+pydnspp_la_LDFLAGS = $(PYTHON_LDFLAGS)
 
 # directly included from source files, so these don't have their own
 # rules
-EXTRA_DIST = libdns_python_common.h
+EXTRA_DIST = pydnspp_common.h
 EXTRA_DIST += edns_python.h
 EXTRA_DIST += messagerenderer_python.cc
 EXTRA_DIST += message_python.cc
@@ -35,7 +24,7 @@ EXTRA_DIST += rrtype_python.cc
 
 # Python prefers .so, while some OSes (specifically MacOS) use a different
 # suffix for dynamic objects.  -module is necessary to work this around.
-libdns_python_la_LDFLAGS += -module
-libdns_python_la_LIBADD = $(top_builddir)/src/lib/dns/libdns++.la
-libdns_python_la_LIBADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
-libdns_python_la_LIBADD += $(PYTHON_LIB)
+pydnspp_la_LDFLAGS += -module
+pydnspp_la_LIBADD = $(top_builddir)/src/lib/dns/libdns++.la
+pydnspp_la_LIBADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+pydnspp_la_LIBADD += $(PYTHON_LIB)

+ 6 - 6
src/lib/dns/python/README

@@ -1,14 +1,14 @@
 
 This is an implementation of the python wrappers for isc::dns.
 
-Currently, when compiled the module is called libdns_python. If we
+When compiled the module is called pydnspp. If we
 decide to always need it we can add a default import under
 lib/python/isc.
 
 To use it from the source tree, you must add src/lib/dns/python/.libs
 to your PYTHONPATH environment variable. Within python you can then use
-> import libdns_python
-> rrc = libdns_python.RRClass("IN")
+> import pydnspp
+> rrc = pydnspp.RRClass("IN")
 etc.
 
 Notes:
@@ -26,8 +26,8 @@ specific Rdata types.
 If you have specific functionality you do need, please ask for it and we
 will add it.
 
-The 'main' module is defined in libdns_python.cc.
-There is a libdns_python_common.[cc|h] for helper functions.
+The 'main' module is defined in pydnspp.cc.
+There is a pydnspp_common.[cc|h] for helper functions.
 
 Implementation notes:
 
@@ -87,7 +87,7 @@ This is repeated for every class we export.
 
 Finally we define the function to add the class, constants, exceptions,
 and enums to the module. This function is called from the init function
-in libdns_python.cc, has the name
+in pydnspp.cc, has the name
 initModulePart_<c++ class name>, returns a boolean
 (true on success, false on failure), and takes the module as a
 PyObject*. There is a convenience function called addClassVariable to

+ 12 - 10
src/lib/dns/python/message_python.cc

@@ -70,7 +70,7 @@ static PyMethodDef MessageFlag_methods[] = {
 
 static PyTypeObject messageflag_type = {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "libdns_python.MessageFlag",
+    "pydnspp.MessageFlag",
     sizeof(s_MessageFlag),              // tp_basicsize
     0,                                  // tp_itemsize
     (destructor)MessageFlag_destroy,    // tp_dealloc
@@ -246,7 +246,7 @@ static PyMethodDef Opcode_methods[] = {
 
 static PyTypeObject opcode_type = {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "libdns_python.Opcode",
+    "pydnspp.Opcode",
     sizeof(s_Opcode),                   // tp_basicsize
     0,                                  // tp_itemsize
     (destructor)Opcode_destroy,         // tp_dealloc
@@ -534,7 +534,7 @@ static PyMethodDef Rcode_methods[] = {
 
 static PyTypeObject rcode_type = {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "libdns_python.Rcode",
+    "pydnspp.Rcode",
     sizeof(s_Rcode),                    // tp_basicsize
     0,                                  // tp_itemsize
     (destructor)Rcode_destroy,          // tp_dealloc
@@ -809,7 +809,7 @@ static PyMethodDef Section_methods[] = {
 
 static PyTypeObject section_type = {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "libdns_python.Section",
+    "pydnspp.Section",
     sizeof(s_Section),                  // tp_basicsize
     0,                                  // tp_itemsize
     (destructor)Section_destroy,        // tp_dealloc
@@ -1099,7 +1099,7 @@ static PyMethodDef Message_methods[] = {
 // Most of the functions are not actually implemented and NULL here.
 static PyTypeObject message_type = {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "libdns_python.Message",
+    "pydnspp.Message",
     sizeof(s_Message),                  // tp_basicsize
     0,                                  // tp_itemsize
     (destructor)Message_destroy,        // tp_dealloc
@@ -1501,7 +1501,7 @@ static PyObject*
 Message_toWire(s_Message* self, PyObject* args) {
     s_MessageRenderer* mr;
     
-    if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, (PyObject**) &mr)) {
+    if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
         try {
             self->message->toWire(*mr->messagerenderer);
             // If we return NULL it is seen as an error, so use this for
@@ -1593,14 +1593,16 @@ initModulePart_Message(PyObject* mod) {
     addClassVariable(message_type, "DEFAULT_MAX_UDPSIZE", Py_BuildValue("I", Message::DEFAULT_MAX_UDPSIZE));
 
     /* Class-specific exceptions */
-    po_MessageTooShort = PyErr_NewException("libdns_python.MessageTooShort", NULL, NULL);
+    po_MessageTooShort = PyErr_NewException("pydnspp.MessageTooShort", NULL, NULL);
     PyModule_AddObject(mod, "MessageTooShort", po_MessageTooShort);
-    po_InvalidMessageSection = PyErr_NewException("libdns_python.InvalidMessageSection", NULL, NULL);
+    po_InvalidMessageSection = PyErr_NewException("pydnspp.InvalidMessageSection", NULL, NULL);
     PyModule_AddObject(mod, "InvalidMessageSection", po_InvalidMessageSection);
-    po_InvalidMessageOperation = PyErr_NewException("libdns_python.InvalidMessageOperation", NULL, NULL);
+    po_InvalidMessageOperation = PyErr_NewException("pydnspp.InvalidMessageOperation", NULL, NULL);
     PyModule_AddObject(mod, "InvalidMessageOperation", po_InvalidMessageOperation);
-    po_InvalidMessageUDPSize = PyErr_NewException("libdns_python.InvalidMessageUDPSize", NULL, NULL);
+    po_InvalidMessageUDPSize = PyErr_NewException("pydnspp.InvalidMessageUDPSize", NULL, NULL);
     PyModule_AddObject(mod, "InvalidMessageUDPSize", po_InvalidMessageUDPSize);
+    po_DNSMessageBADVERS = PyErr_NewException("pydnspp.DNSMessageBADVERS", NULL, NULL);
+    PyModule_AddObject(mod, "DNSMessageBADVERS", po_DNSMessageBADVERS);
 
     Py_INCREF(&message_type);
     PyModule_AddObject(mod, "Message",

+ 1 - 1
src/lib/dns/python/messagerenderer_python.cc

@@ -65,7 +65,7 @@ static PyMethodDef MessageRenderer_methods[] = {
 
 static PyTypeObject messagerenderer_type = {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "libdns_python.MessageRenderer",
+    "pydnspp.MessageRenderer",
     sizeof(s_MessageRenderer),          // tp_basicsize
     0,                                  // tp_itemsize
     (destructor)MessageRenderer_destroy,// tp_dealloc

+ 14 - 14
src/lib/dns/python/name_python.cc

@@ -68,7 +68,7 @@ static PyMethodDef NameComparisonResult_methods[] = {
 
 static PyTypeObject name_comparison_result_type = {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "libdns_python.NameComparisonResult",
+    "pydnspp.NameComparisonResult",
     sizeof(s_NameComparisonResult),           // tp_basicsize
     0,                                        // tp_itemsize
     (destructor)NameComparisonResult_destroy, // tp_dealloc
@@ -222,7 +222,7 @@ static PyMethodDef Name_methods[] = {
 
 static PyTypeObject name_type = {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "libdns_python.Name",
+    "pydnspp.Name",
     sizeof(s_Name),                     // tp_basicsize
     0,                                  // tp_itemsize
     (destructor)Name_destroy,           // tp_dealloc
@@ -421,7 +421,7 @@ Name_toWire(s_Name* self, PyObject* args) {
         // to prevent memory leak
         Py_DECREF(name_bytes);
         return (result);
-    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, (PyObject**) &mr)) {
+    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
         self->name->toWire(*mr->messagerenderer);
         // If we return NULL it is seen as an error, so use this for
         // None returns
@@ -437,7 +437,7 @@ static PyObject*
 Name_compare(s_Name* self, PyObject* args) {
     s_Name* other;
 
-    if (!PyArg_ParseTuple(args, "O!", &name_type, (PyObject* *) &other))
+    if (!PyArg_ParseTuple(args, "O!", &name_type, &other))
         return (NULL);
 
     s_NameComparisonResult* ret = PyObject_New(s_NameComparisonResult, &name_comparison_result_type);
@@ -452,7 +452,7 @@ static PyObject*
 Name_equals(s_Name* self, PyObject* args) {
     s_Name* other;
 
-    if (!PyArg_ParseTuple(args, "O!", &name_type, (PyObject* *) &other))
+    if (!PyArg_ParseTuple(args, "O!", &name_type, &other))
         return (NULL);
 
     if (self->name->equals(*other->name))
@@ -565,7 +565,7 @@ static PyObject*
 Name_concatenate(s_Name* self, PyObject* args) {
     s_Name* other;
 
-    if (!PyArg_ParseTuple(args, "O!", &name_type, (PyObject**) &other))
+    if (!PyArg_ParseTuple(args, "O!", &name_type, &other))
         return (NULL);
 
     s_Name* ret = PyObject_New(s_Name, &name_type);
@@ -651,30 +651,30 @@ initModulePart_Name(PyObject* mod) {
     
 
     // Add the exceptions to the module
-    po_EmptyLabel = PyErr_NewException("libdns_python.EmptyLabel", NULL, NULL);
+    po_EmptyLabel = PyErr_NewException("pydnspp.EmptyLabel", NULL, NULL);
     PyModule_AddObject(mod, "EmptyLabel", po_EmptyLabel);
 
-    po_TooLongName = PyErr_NewException("libdns_python.TooLongName", NULL, NULL);
+    po_TooLongName = PyErr_NewException("pydnspp.TooLongName", NULL, NULL);
     PyModule_AddObject(mod, "TooLongName", po_TooLongName);
 
-    po_TooLongLabel = PyErr_NewException("libdns_python.TooLongLabel", NULL, NULL);
+    po_TooLongLabel = PyErr_NewException("pydnspp.TooLongLabel", NULL, NULL);
     PyModule_AddObject(mod, "TooLongLabel", po_TooLongLabel);
 
-    po_BadLabelType = PyErr_NewException("libdns_python.BadLabelType", NULL, NULL);
+    po_BadLabelType = PyErr_NewException("pydnspp.BadLabelType", NULL, NULL);
     PyModule_AddObject(mod, "BadLabelType", po_BadLabelType);
 
-    po_BadEscape = PyErr_NewException("libdns_python.BadEscape", NULL, NULL);
+    po_BadEscape = PyErr_NewException("pydnspp.BadEscape", NULL, NULL);
     PyModule_AddObject(mod, "BadEscape", po_BadEscape);
 
-    po_IncompleteName = PyErr_NewException("libdns_python.IncompleteName", NULL, NULL);
+    po_IncompleteName = PyErr_NewException("pydnspp.IncompleteName", NULL, NULL);
     PyModule_AddObject(mod, "IncompleteName", po_IncompleteName);
 
-    po_InvalidBufferPosition = PyErr_NewException("libdns_python.InvalidBufferPosition", NULL, NULL);
+    po_InvalidBufferPosition = PyErr_NewException("pydnspp.InvalidBufferPosition", NULL, NULL);
     PyModule_AddObject(mod, "InvalidBufferPosition", po_InvalidBufferPosition);
 
     // This one could have gone into the message_python.cc file, but is
     // already needed here.
-    po_DNSMessageFORMERR = PyErr_NewException("libdns_python.DNSMessageFORMERR", NULL, NULL);
+    po_DNSMessageFORMERR = PyErr_NewException("pydnspp.DNSMessageFORMERR", NULL, NULL);
     PyModule_AddObject(mod, "DNSMessageFORMERR", po_DNSMessageFORMERR);
 
     return (true);

+ 141 - 0
src/lib/dns/python/pydnspp.cc

@@ -0,0 +1,141 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+
+// We want a lot of different parts of the DNS API in the python
+// module, but not one big 10000-line file.
+// So we split it up in several 'mini-modules'
+// These would be the same as a single module, except for
+// the init function, which has to be modified to a unique
+// name initModulePart_<name>, and return true/false instead of
+// NULL/*mod
+//
+// And of course care has to be taken that all identifiers be unique
+
+// $Id$
+
+#define PY_SSIZE_T_CLEAN
+#include <Python.h>
+#include <structmember.h>
+
+#include <config.h>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/buffer.h>
+#include <dns/exceptions.h>
+#include <dns/name.h>
+#include <dns/messagerenderer.h>
+
+#include <dns/python/pydnspp_common.h>
+
+// For our 'general' isc::Exceptions
+static PyObject* po_IscException;
+static PyObject* po_InvalidParameter;
+
+// For our own isc::dns::Exception
+static PyObject* po_DNSMessageBADVERS;
+
+// order is important here!
+#include <dns/python/messagerenderer_python.cc>
+#include <dns/python/name_python.cc>           // needs Messagerenderer
+#include <dns/python/rrclass_python.cc>        // needs Messagerenderer
+#include <dns/python/rrtype_python.cc>         // needs Messagerenderer
+#include <dns/python/rrttl_python.cc>          // needs Messagerenderer
+#include <dns/python/rdata_python.cc>          // needs Type, Class
+#include <dns/python/rrset_python.cc>          // needs Rdata, RRTTL
+#include <dns/python/question_python.cc>       // needs RRClass, RRType, RRTTL,
+                                               // Name
+#include <dns/python/edns_python.cc>           // needs Messagerenderer, Rcode
+#include <dns/python/message_python.cc>        // needs RRset, Question
+
+//
+// Definition of the module
+//
+static PyModuleDef pydnspp = {
+    { PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
+    "pydnspp",
+    "Python bindings for the classes in the isc::dns namespace.\n\n"
+    "These bindings match the original C++ API as closely as possible, "
+    "but are not complete. Some classes are unnecessary (InputBuffer "
+    "and OutputBuffer for instance), and others may be necessary, but "
+    "were not up to now.",
+    -1,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL
+};
+
+PyMODINIT_FUNC
+PyInit_pydnspp(void) {
+    PyObject *mod = PyModule_Create(&pydnspp);
+    if (mod == NULL) {
+        return (NULL);
+    }
+
+    // Add the exceptions to the class
+    po_IscException = PyErr_NewException("pydnspp.IscException", NULL, NULL);
+    PyModule_AddObject(mod, "IscException", po_IscException);
+
+    po_InvalidParameter = PyErr_NewException("pydnspp.InvalidParameter",
+                                             NULL, NULL);
+    PyModule_AddObject(mod, "InvalidParameter", po_InvalidParameter);
+
+    // for each part included above, we call its specific initializer
+
+    if (!initModulePart_Name(mod)) {
+        return (NULL);
+    }
+
+    if (!initModulePart_MessageRenderer(mod)) {
+        return (NULL);
+    }
+
+    if (!initModulePart_RRClass(mod)) {
+        return (NULL);
+    }
+
+    if (!initModulePart_RRType(mod)) {
+        return (NULL);
+    }
+
+    if (!initModulePart_RRTTL(mod)) {
+        return (NULL);
+    }
+
+    if (!initModulePart_Rdata(mod)) {
+        return (NULL);
+    }
+
+    if (!initModulePart_RRset(mod)) {
+        return (NULL);
+    }
+
+    if (!initModulePart_Question(mod)) {
+        return (NULL);
+    }
+
+    if (!initModulePart_Message(mod)) {
+        return (NULL);
+    }
+
+    if (!initModulePart_EDNS(mod)) {
+        return (NULL);
+    }
+
+    return (mod);
+}
+

+ 1 - 1
src/lib/dns/python/libdns_python_common.cc

@@ -15,7 +15,7 @@
 // $Id$
 
 #include <Python.h>
-#include <libdns_python_common.h>
+#include <pydnspp_common.h>
 
 int
 readDataFromSequence(uint8_t *data, size_t len, PyObject* sequence) {

src/lib/dns/python/libdns_python_common.h → src/lib/dns/python/pydnspp_common.h


+ 2 - 3
src/lib/dns/python/question_python.cc

@@ -76,7 +76,7 @@ static PyMethodDef Question_methods[] = {
 // Most of the functions are not actually implemented and NULL here.
 static PyTypeObject question_type = {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "libdns_python.Question",
+    "pydnspp.Question",
     sizeof(s_Question),                 // tp_basicsize
     0,                                  // tp_itemsize
     (destructor)Question_destroy,       // tp_dealloc
@@ -254,8 +254,7 @@ Question_toWire(s_Question* self, PyObject* args) {
         // to prevent memory leak
         Py_DECREF(n);
         return (result);
-    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type,
-                                reinterpret_cast<PyObject**>(&mr))) {
+    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
         self->question->toWire(*mr->messagerenderer);
         // If we return NULL it is seen as an error, so use this for
         // None returns

+ 5 - 5
src/lib/dns/python/rdata_python.cc

@@ -92,7 +92,7 @@ static PyMethodDef Rdata_methods[] = {
 // Most of the functions are not actually implemented and NULL here.
 static PyTypeObject rdata_type = {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "libdns_python.Rdata",
+    "pydnspp.Rdata",
     sizeof(s_Rdata),                    // tp_basicsize
     0,                                  // tp_itemsize
     (destructor)Rdata_destroy,          // tp_dealloc
@@ -204,7 +204,7 @@ Rdata_toWire(s_Rdata* self, PyObject* args) {
         // to prevent memory leak
         Py_DECREF(rd_bytes);
         return (result);
-    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, (PyObject**) &mr)) {
+    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
         self->rdata->toWire(*mr->messagerenderer);
         // If we return NULL it is seen as an error, so use this for
         // None returns
@@ -276,13 +276,13 @@ initModulePart_Rdata(PyObject* mod) {
                        reinterpret_cast<PyObject*>(&rdata_type));
 
     // Add the exceptions to the class
-    po_InvalidRdataLength = PyErr_NewException("libdns_python.InvalidRdataLength", NULL, NULL);
+    po_InvalidRdataLength = PyErr_NewException("pydnspp.InvalidRdataLength", NULL, NULL);
     PyModule_AddObject(mod, "InvalidRdataLength", po_InvalidRdataLength);
 
-    po_InvalidRdataText = PyErr_NewException("libdns_python.InvalidRdataText", NULL, NULL);
+    po_InvalidRdataText = PyErr_NewException("pydnspp.InvalidRdataText", NULL, NULL);
     PyModule_AddObject(mod, "InvalidRdataText", po_InvalidRdataText);
 
-    po_CharStringTooLong = PyErr_NewException("libdns_python.CharStringTooLong", NULL, NULL);
+    po_CharStringTooLong = PyErr_NewException("pydnspp.CharStringTooLong", NULL, NULL);
     PyModule_AddObject(mod, "CharStringTooLong", po_CharStringTooLong);
 
     

+ 6 - 6
src/lib/dns/python/rrclass_python.cc

@@ -100,7 +100,7 @@ static PyMethodDef RRClass_methods[] = {
 // Most of the functions are not actually implemented and NULL here.
 static PyTypeObject rrclass_type = {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "libdns_python.RRClass",
+    "pydnspp.RRClass",
     sizeof(s_RRClass),                  // tp_basicsize
     0,                                  // tp_itemsize
     (destructor)RRClass_destroy,        // tp_dealloc
@@ -157,7 +157,7 @@ RRClass_init(s_RRClass* self, PyObject* args) {
     unsigned int i;
     PyObject* bytes = NULL;
     // The constructor argument can be a string ("IN"), an integer (1),
-    // or a sequence of numbers between 0 and 255 (wire code)
+    // or a sequence of numbers between 0 and 65535 (wire code)
 
     // Note that PyArg_ParseType can set PyError, and we need to clear
     // that if we try several like here. Otherwise the *next* python
@@ -170,7 +170,7 @@ RRClass_init(s_RRClass* self, PyObject* args) {
         } else if (PyArg_ParseTuple(args, "I", &i)) {
             PyErr_Clear();
             if (i > 65535) {
-                PyErr_SetString(po_InvalidRRClass, "Class number too high");
+                PyErr_SetString(po_InvalidRRClass, "RR class number too high");
                 return (-1);
             }
             self->rrclass = new RRClass(i);
@@ -236,7 +236,7 @@ RRClass_toWire(s_RRClass* self, PyObject* args) {
         // to prevent memory leak
         Py_DECREF(n);
         return (result);
-    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, (PyObject**) &mr)) {
+    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
         self->rrclass->toWire(*mr->messagerenderer);
         // If we return NULL it is seen as an error, so use this for
         // None returns
@@ -332,10 +332,10 @@ static PyObject* RRClass_ANY(s_RRClass *self UNUSED_PARAM) {
 bool
 initModulePart_RRClass(PyObject* mod) {
     // Add the exceptions to the module
-    po_InvalidRRClass = PyErr_NewException("libdns_python.InvalidRRClass", NULL, NULL);
+    po_InvalidRRClass = PyErr_NewException("pydnspp.InvalidRRClass", NULL, NULL);
     Py_INCREF(po_InvalidRRClass);
     PyModule_AddObject(mod, "InvalidRRClass", po_InvalidRRClass);
-    po_IncompleteRRClass = PyErr_NewException("libdns_python.IncompleteRRClass", NULL, NULL);
+    po_IncompleteRRClass = PyErr_NewException("pydnspp.IncompleteRRClass", NULL, NULL);
     Py_INCREF(po_IncompleteRRClass);
     PyModule_AddObject(mod, "IncompleteRRClass", po_IncompleteRRClass);
 

+ 3 - 3
src/lib/dns/python/rrset_python.cc

@@ -94,7 +94,7 @@ static PyMethodDef RRset_methods[] = {
 
 static PyTypeObject rrset_type = {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "libdns_python.RRset",
+    "pydnspp.RRset",
     sizeof(s_RRset),                    // tp_basicsize
     0,                                  // tp_itemsize
     (destructor)RRset_destroy,          // tp_dealloc
@@ -315,7 +315,7 @@ RRset_toWire(s_RRset* self, PyObject* args) {
             // to prevent memory leak
             Py_DECREF(n);
             return (result);
-        } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, (PyObject**) &mr)) {
+        } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
             self->rrset->toWire(*mr->messagerenderer);
             // If we return NULL it is seen as an error, so use this for
             // None returns
@@ -379,7 +379,7 @@ RRset_getRdata(s_RRset* self) {
 bool
 initModulePart_RRset(PyObject* mod) {
     // Add the exceptions to the module
-    po_EmptyRRset = PyErr_NewException("libdns_python.EmptyRRset", NULL, NULL);
+    po_EmptyRRset = PyErr_NewException("pydnspp.EmptyRRset", NULL, NULL);
     PyModule_AddObject(mod, "EmptyRRset", po_EmptyRRset);
 
     // Add the enums to the module

+ 0 - 0
src/lib/dns/python/rrttl_python.cc


Some files were not shown because too many files changed in this diff