[svn] commit: r3437 - in /branches/trac358: ./ src/lib/bench/ src/lib/bench/example/ src/lib/bench/tests/ src/lib/datasrc/ src/lib/datasrc/tests/ src/lib/exceptions/ src/lib/exceptions/tests/ src/lib/python/isc/ src/lib/python/isc/Util/ src/lib/python/isc/config/ src/lib/python/isc/config/tests/ src/lib/python/isc/datasrc/ src/lib/python/isc/log/ src/lib/python/isc/log/tests/ src/lib/python/isc/net/ src/lib/python/isc/notify/ src/lib/python/isc/notify/tests/ src/lib/xfr/

BIND 10 source code commits bind10-changes at lists.isc.org
Thu Nov 4 06:21:08 UTC 2010


Author: jinmei
Date: Thu Nov  4 06:21:08 2010
New Revision: 3437

Log:
sync with trunk

Added:
    branches/trac358/src/lib/python/isc/net/
      - copied from r3436, trunk/src/lib/python/isc/net/
Removed:
    branches/trac358/src/lib/python/isc/Util/
Modified:
    branches/trac358/ChangeLog
    branches/trac358/configure.ac
    branches/trac358/src/lib/bench/Makefile.am
    branches/trac358/src/lib/bench/example/search_bench.cc
    branches/trac358/src/lib/bench/tests/Makefile.am
    branches/trac358/src/lib/datasrc/Makefile.am
    branches/trac358/src/lib/datasrc/static_datasrc.cc
    branches/trac358/src/lib/datasrc/tests/Makefile.am
    branches/trac358/src/lib/datasrc/tests/run_unittests.cc
    branches/trac358/src/lib/exceptions/exceptions.cc
    branches/trac358/src/lib/exceptions/tests/run_unittests.cc
    branches/trac358/src/lib/python/isc/Makefile.am
    branches/trac358/src/lib/python/isc/config/ccsession.py
    branches/trac358/src/lib/python/isc/config/module_spec.py
    branches/trac358/src/lib/python/isc/config/tests/ccsession_test.py
    branches/trac358/src/lib/python/isc/config/tests/cfgmgr_test.py
    branches/trac358/src/lib/python/isc/config/tests/unittest_fakesession.py
    branches/trac358/src/lib/python/isc/datasrc/master.py
    branches/trac358/src/lib/python/isc/log/log.py
    branches/trac358/src/lib/python/isc/log/tests/log_test.py
    branches/trac358/src/lib/python/isc/notify/notify_out.py
    branches/trac358/src/lib/python/isc/notify/tests/notify_out_test.py
    branches/trac358/src/lib/xfr/Makefile.am
    branches/trac358/src/lib/xfr/python_xfr.cc
    branches/trac358/src/lib/xfr/xfrout_client.cc

Modified: branches/trac358/ChangeLog
==============================================================================
--- branches/trac358/ChangeLog (original)
+++ branches/trac358/ChangeLog Thu Nov  4 06:21:08 2010
@@ -1,3 +1,70 @@
+  114.	[build]		jinmei
+	Supported clang++.  Note: Boost >= 1.44 is required.
+	(Trac #365, svn r3383)
+
+  113.	[func]*		zhanglikun
+	Folder name 'utils'(the folder in /src/lib/python/isc/) has been 
+	renamed	to 'util'. Programs that used 'import isc.utils.process'
+	now need to use 'import isc.util.process'. The folder
+	/src/lib/python/isc/Util is removed since it isn't used by any
+	program. (Trac #364, r3382)
+
+  112.	[func]		zhang likun
+	Add one mixin class to override the naive serve_forever() provided
+	in python library socketserver. Instead of polling for shutdwon
+	every poll_interval seconds, one socketpair is used to wake up
+	the waiting server.(Trac #352, svn r3366)
+
+  111.	[bug]*   zhanglikun, Michal Vaner
+	Make sure process xfrin/xfrout/zonemgr/cmdctl can be stoped
+	properly when user enter "ctrl+c" or 'Boss shutdown' command
+	through	bindctl.
+
+	The ZonemgrRefresh.run_timer and NotifyOut.dispatcher spawn
+	a thread themselves.
+	(Trac #335, svn r3273)
+
+  110.  [func]      Michal Vaner
+	Added isc.net.check module to check ip addresses and ports for
+	correctness and isc.net.addr to hold IP address. The bind10, xfrin
+	and cmdctl programs are modified to use it.
+	(Trac #353, svn r3240)
+
+  109.  [func]		naokikambe
+	Added the initial version of the stats module for the statistics
+	feature of BIND 10, which supports the restricted features and
+	items and reports via bindctl command (Trac #191, r3218)
+	Added the document of the stats module, which is about how stats
+	module collects the data (Trac #170, [wiki:StatsModule])
+
+  108.	[func]		jerry
+	src/bin/zonemgr: Provide customizable configurations for
+	lowerbound_refresh, lowerbound_retry, max_transfer_timeout and
+	jitter_scope. (Trac #340, r3205)
+
+  107.  [func]       zhang likun
+	Remove the parameter 'db_file' for command 'retransfer' of
+	xfrin module. xfrin.spec will not be generated by script.
+	(Trac #329, r3171)
+
+  106.  [bug]       zhang likun
+	When xfrin can't connect with one zone's master, it should tell
+	the bad news to zonemgr, so that zonemgr can reset the timer for
+	that zone. (Trac #329, r3170)
+
+  105.  [bug]       Michal Vaner
+	Python processes: they no longer take 100% CPU while idle
+	due to a busy loop in reading command session in a nonblocking way.
+	(Trac #349, svn r3153), (Trac #382, svn r3294)
+
+  104.	[bug]		jerry
+	bin/zonemgr: zonemgr should be attempting to refresh expired zones.
+	(Trac #336, r3139)
+				   
+  103.	[bug]		jerry
+	lib/python/isc/log: Fixed an issue with python logging,
+	python log shouldn't die with OSError.(Trac #267, r3137)
+				   
   102.	[build]		jinmei
 	Disable threads in ASIO to minimize build time dependency.
 	(Trac #345, r3100)
@@ -89,8 +156,8 @@
 	zone axfr/ixfr finishing, the server will notify its slaves.
 	(Trac #289, svn r2737)
 
-  86.   [func]		jerry
-    	bin/zonemgr: Added zone manager module. The zone manager is one 
+  86.	[func]		jerry
+	bin/zonemgr: Added zone manager module. The zone manager is one 
 	of the co-operating processes of BIND10, which keeps track of 
 	timers and other information necessary for BIND10 to act as a 
 	slave. (Trac #215, svn r2737)

Modified: branches/trac358/configure.ac
==============================================================================
--- branches/trac358/configure.ac (original)
+++ branches/trac358/configure.ac Thu Nov  4 06:21:08 2010
@@ -2,7 +2,7 @@
 # Process this file with autoconf to produce a configure script.
 
 AC_PREREQ([2.59])
-AC_INIT(bind10-devel, 20100701, bind10-dev at isc.org)
+AC_INIT(bind10-devel, 20101013, bind10-dev at isc.org)
 AC_CONFIG_SRCDIR(README)
 AM_INIT_AUTOMAKE
 AC_CONFIG_HEADERS([config.h])
@@ -17,6 +17,8 @@
 # Identify the compiler: this check must be after AC_PROG_CXX and AC_LANG.
 AM_CONDITIONAL(USE_GXX, test "X${GXX}" = "Xyes")
 AC_CHECK_DECL([__SUNPRO_CC], [SUNCXX="yes"], [SUNCXX="no"])
+AC_CHECK_DECL([__clang__], [CLANGPP="yes"], [CLANGPP="no"])
+AM_CONDITIONAL(USE_CLANGPP, test "X${CLANGPP}" = "Xyes")
 
 # Linker options
 
@@ -261,7 +263,6 @@
 [  --with-gtest=PATH       specify a path to gtest header files (PATH/include) and library (PATH/lib)],
     gtest_path="$withval", gtest_path="no")
 
-
 USE_LCOV="no"
 if test "$lcov" != "no"; then
 	# force gtest if not set
@@ -314,12 +315,14 @@
 		fi
 	done
 fi
+CPPFLAGS_SAVES="$CPPFLAGS"
 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.]))
+CPPFLAGS="$CPPFLAGS_SAVES"
 AC_SUBST(BOOST_INCLUDES)
 
 #
@@ -435,7 +438,7 @@
 fi
 
 AC_ARG_ENABLE(man, [AC_HELP_STRING([--enable-man],
-  [regenerate man pages [default=no]])] ,enable_man=yes, enable_man=no)
+  [regenerate man pages [default=no]])], enable_man=yes, enable_man=no)
 
 AM_CONDITIONAL(ENABLE_MAN, test x$enable_man != xno)
 
@@ -472,6 +475,13 @@
                  src/bin/xfrout/tests/Makefile
                  src/bin/zonemgr/Makefile
                  src/bin/zonemgr/tests/Makefile
+                 src/bin/stats/Makefile
+                 src/bin/stats/tests/Makefile
+                 src/bin/stats/tests/isc/Makefile
+                 src/bin/stats/tests/isc/cc/Makefile
+                 src/bin/stats/tests/isc/config/Makefile
+                 src/bin/stats/tests/isc/util/Makefile
+                 src/bin/stats/tests/testdata/Makefile
                  src/bin/usermgr/Makefile
                  src/bin/tests/Makefile
                  src/lib/Makefile
@@ -482,8 +492,8 @@
                  src/lib/cc/tests/Makefile
                  src/lib/python/Makefile
                  src/lib/python/isc/Makefile
-                 src/lib/python/isc/utils/Makefile
-                 src/lib/python/isc/utils/tests/Makefile
+                 src/lib/python/isc/util/Makefile
+                 src/lib/python/isc/util/tests/Makefile
                  src/lib/python/isc/datasrc/Makefile
                  src/lib/python/isc/cc/Makefile
                  src/lib/python/isc/cc/tests/Makefile
@@ -491,6 +501,8 @@
                  src/lib/python/isc/config/tests/Makefile
                  src/lib/python/isc/log/Makefile
                  src/lib/python/isc/log/tests/Makefile
+                 src/lib/python/isc/net/Makefile
+                 src/lib/python/isc/net/tests/Makefile
                  src/lib/python/isc/notify/Makefile
                  src/lib/python/isc/notify/tests/Makefile
                  src/lib/config/Makefile
@@ -515,7 +527,6 @@
            src/bin/cmdctl/cmdctl.spec.pre
            src/bin/xfrin/tests/xfrin_test
            src/bin/xfrin/xfrin.py
-           src/bin/xfrin/xfrin.spec.pre
            src/bin/xfrin/run_b10-xfrin.sh
            src/bin/xfrout/xfrout.py
            src/bin/xfrout/xfrout.spec.pre
@@ -525,6 +536,12 @@
            src/bin/zonemgr/zonemgr.spec.pre
            src/bin/zonemgr/tests/zonemgr_test
            src/bin/zonemgr/run_b10-zonemgr.sh
+           src/bin/stats/stats.py
+           src/bin/stats/stats_stub.py
+           src/bin/stats/stats.spec.pre
+           src/bin/stats/run_b10-stats.sh
+           src/bin/stats/run_b10-stats_stub.sh
+           src/bin/stats/tests/stats_test
            src/bin/bind10/bind10.py
            src/bin/bind10/tests/bind10_test
            src/bin/bind10/run_bind10.sh
@@ -558,6 +575,9 @@
            chmod +x src/bin/xfrin/run_b10-xfrin.sh
            chmod +x src/bin/xfrout/run_b10-xfrout.sh
            chmod +x src/bin/zonemgr/run_b10-zonemgr.sh
+           chmod +x src/bin/stats/tests/stats_test
+           chmod +x src/bin/stats/run_b10-stats.sh
+           chmod +x src/bin/stats/run_b10-stats_stub.sh
            chmod +x src/bin/bind10/run_bind10.sh
            chmod +x src/bin/cmdctl/tests/cmdctl_test
            chmod +x src/bin/xfrin/tests/xfrin_test
@@ -588,6 +608,8 @@
   Name:          $PACKAGE_NAME
   Version:       $PACKAGE_VERSION
 
+C++ Compiler:    $CXX
+
 Flags:
   DEFS:          $DEFS
   CPPFLAGS:      $CPPFLAGS
@@ -597,6 +619,7 @@
   Python:        ${PYTHON_INCLUDES}
                  ${PYTHON_LDFLAGS}
                  ${PYTHON_LIB}
+  Boost:         ${BOOST_INCLUDES}
   SQLite:        $SQLITE_CFLAGS
                  $SQLITE_LIBS
 
@@ -616,4 +639,3 @@
   Now you can type "make" to build BIND 10
 
 EOF
-

Modified: branches/trac358/src/lib/bench/Makefile.am
==============================================================================
--- branches/trac358/src/lib/bench/Makefile.am (original)
+++ branches/trac358/src/lib/bench/Makefile.am Thu Nov  4 06:21:08 2010
@@ -1,6 +1,7 @@
 SUBDIRS = . tests example
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
 CLEANFILES = *.gcno *.gcda

Modified: branches/trac358/src/lib/bench/example/search_bench.cc
==============================================================================
--- branches/trac358/src/lib/bench/example/search_bench.cc (original)
+++ branches/trac358/src/lib/bench/example/search_bench.cc Thu Nov  4 06:21:08 2010
@@ -16,6 +16,7 @@
 
 #include <unistd.h>             // for getpid
 
+#include <cassert>
 #include <cstdlib>              // for rand
 #include <algorithm>
 #include <iostream>
@@ -42,9 +43,11 @@
         vector<int>::const_iterator end_key = keys_.end();
         for (iter = keys_.begin(); iter != end_key; ++iter) {
             if (Sorted) {
-                binary_search(data_.begin(), data_.end(), *iter);
+                // perform simple sanity check with assert() to ensure
+                // compiler optimization won't skip the search.
+                assert(binary_search(data_.begin(), data_.end(), *iter));
             } else {
-                find(data_.begin(), data_.end(), *iter);
+                assert(find(data_.begin(), data_.end(), *iter) != data_.end());
             }
         }
         return (keys_.size());
@@ -63,7 +66,7 @@
         vector<int>::const_iterator iter;
         vector<int>::const_iterator end_key = keys_.end();
         for (iter = keys_.begin(); iter != end_key; ++iter) {
-            data_.find(*iter);
+            assert(data_.find(*iter) != data_.end());
         }        
         return (keys_.size());
     }

Modified: branches/trac358/src/lib/bench/tests/Makefile.am
==============================================================================
--- branches/trac358/src/lib/bench/tests/Makefile.am (original)
+++ branches/trac358/src/lib/bench/tests/Makefile.am Thu Nov  4 06:21:08 2010
@@ -1,4 +1,5 @@
 AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
 AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(srcdir)/testdata\"
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 

Modified: branches/trac358/src/lib/datasrc/Makefile.am
==============================================================================
--- branches/trac358/src/lib/datasrc/Makefile.am (original)
+++ branches/trac358/src/lib/datasrc/Makefile.am Thu Nov  4 06:21:08 2010
@@ -2,6 +2,7 @@
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
+AM_CPPFLAGS += $(BOOST_INCLUDES)
 AM_CPPFLAGS += $(SQLITE_CFLAGS)
 
 AM_CXXFLAGS = $(B10_CXXFLAGS)

Modified: branches/trac358/src/lib/datasrc/static_datasrc.cc
==============================================================================
--- branches/trac358/src/lib/datasrc/static_datasrc.cc (original)
+++ branches/trac358/src/lib/datasrc/static_datasrc.cc Thu Nov  4 06:21:08 2010
@@ -110,14 +110,12 @@
                                "0 28800 7200 604800 86400"));
 }
 
-StaticDataSrc::StaticDataSrc()
-{
+StaticDataSrc::StaticDataSrc() {
     setClass(RRClass::CH());
     impl_ = new StaticDataSrcImpl;
 }
 
-StaticDataSrc::~StaticDataSrc()
-{
+StaticDataSrc::~StaticDataSrc() {
     delete impl_;
 }
 

Modified: branches/trac358/src/lib/datasrc/tests/Makefile.am
==============================================================================
--- branches/trac358/src/lib/datasrc/tests/Makefile.am (original)
+++ branches/trac358/src/lib/datasrc/tests/Makefile.am Thu Nov  4 06:21:08 2010
@@ -1,5 +1,7 @@
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/lib/dns
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += $(SQLITE_CFLAGS)
 AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(srcdir)/testdata\"
 
 AM_CXXFLAGS = $(B10_CXXFLAGS)

Modified: branches/trac358/src/lib/datasrc/tests/run_unittests.cc
==============================================================================
--- branches/trac358/src/lib/datasrc/tests/run_unittests.cc (original)
+++ branches/trac358/src/lib/datasrc/tests/run_unittests.cc Thu Nov  4 06:21:08 2010
@@ -19,8 +19,7 @@
 #include <dns/tests/unittest_util.h>
 
 int
-main(int argc, char* argv[])
-{
+main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
     isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
 

Modified: branches/trac358/src/lib/exceptions/exceptions.cc
==============================================================================
--- branches/trac358/src/lib/exceptions/exceptions.cc (original)
+++ branches/trac358/src/lib/exceptions/exceptions.cc Thu Nov  4 06:21:08 2010
@@ -23,8 +23,7 @@
 namespace isc {
 
 const char*
-Exception::what() const throw()
-{
+Exception::what() const throw() {
     const char* whatstr = "isc::Exception";
 
     // XXX: even though it's very unlikely that c_str() throws an exception,

Modified: branches/trac358/src/lib/exceptions/tests/run_unittests.cc
==============================================================================
--- branches/trac358/src/lib/exceptions/tests/run_unittests.cc (original)
+++ branches/trac358/src/lib/exceptions/tests/run_unittests.cc Thu Nov  4 06:21:08 2010
@@ -17,8 +17,7 @@
 #include <gtest/gtest.h>
 
 int
-main(int argc, char* argv[])
-{
+main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
     return (RUN_ALL_TESTS());
 }

Modified: branches/trac358/src/lib/python/isc/Makefile.am
==============================================================================
--- branches/trac358/src/lib/python/isc/Makefile.am (original)
+++ branches/trac358/src/lib/python/isc/Makefile.am Thu Nov  4 06:21:08 2010
@@ -1,4 +1,4 @@
-SUBDIRS = datasrc cc config log notify utils # Util
+SUBDIRS = datasrc cc config log net notify util 
 
 python_PYTHON = __init__.py
 

Modified: branches/trac358/src/lib/python/isc/config/ccsession.py
==============================================================================
--- branches/trac358/src/lib/python/isc/config/ccsession.py (original)
+++ branches/trac358/src/lib/python/isc/config/ccsession.py Thu Nov  4 06:21:08 2010
@@ -1,4 +1,5 @@
 # Copyright (C) 2009  Internet Systems Consortium.
+# Copyright (C) 2010  CZ NIC
 #
 # Permission to use, copy, modify, and distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
@@ -169,20 +170,35 @@
            time-critical, it is strongly recommended to only use
            check_command(), and not look at the socket at all."""
         return self._session._socket
-    
+
     def close(self):
         """Close the session to the command channel"""
         self._session.close()
 
-    def check_command(self):
-        """Check whether there is a command or configuration update
-           on the channel. Call the corresponding callback function if
-           there is. This function does a non-blocking read on the
-           cc session, and returns nothing. It will respond to any
-           command by either an error or the answer message returned
-           by the callback, unless the latter is None."""
-        msg, env = self._session.group_recvmsg(True)
-        
+    def check_command(self, nonblock=True):
+        """Check whether there is a command or configuration update on
+           the channel. This function does a read on the cc session, and
+           returns nothing.
+           It calls check_command_without_recvmsg()
+           to parse the received message.
+           
+           If nonblock is True, it just checks if there's a command
+           and does nothing if there isn't. If nonblock is False, it
+           waits until it arrives. It temporarily sets timeout to infinity,
+           because commands may not come in arbitrary long time."""
+        timeout_orig = self._session.get_timeout()
+        self._session.set_timeout(0)
+        try:
+            msg, env = self._session.group_recvmsg(nonblock)
+        finally:
+            self._session.set_timeout(timeout_orig)
+        self.check_command_without_recvmsg(msg, env)
+
+    def check_command_without_recvmsg(self, msg, env):
+        """Parse the given message to see if there is a command or a
+           configuration update. Calls the corresponding handler
+           functions if present. Responds on the channel if the
+           handler returns a message.""" 
         # should we default to an answer? success-by-default? unhandled error?
         if msg is not None and not 'result' in msg:
             answer = None
@@ -200,7 +216,8 @@
                             newc = self._remote_module_configs[module_name].get_local_config()
                             isc.cc.data.merge(newc, new_config)
                             self._remote_module_configs[module_name].set_local_config(newc)
-                            return
+                        # For other modules, we're not supposed to answer
+                        return
 
                     # ok, so apparently this update is for us.
                     errors = []

Modified: branches/trac358/src/lib/python/isc/config/module_spec.py
==============================================================================
--- branches/trac358/src/lib/python/isc/config/module_spec.py (original)
+++ branches/trac358/src/lib/python/isc/config/module_spec.py Thu Nov  4 06:21:08 2010
@@ -316,7 +316,9 @@
     item_name = spec['item_name']
     item_optional = spec['item_optional']
 
-    if item_name in data:
+    if not data and item_optional:
+        return True
+    elif item_name in data:
         return _validate_item(spec, full, data[item_name], errors)
     elif full and not item_optional:
         if errors != None:

Modified: branches/trac358/src/lib/python/isc/config/tests/ccsession_test.py
==============================================================================
--- branches/trac358/src/lib/python/isc/config/tests/ccsession_test.py (original)
+++ branches/trac358/src/lib/python/isc/config/tests/ccsession_test.py Thu Nov  4 06:21:08 2010
@@ -23,7 +23,7 @@
 import os
 from isc.config.ccsession import *
 from isc.config.config_data import BIND10_CONFIG_DATA_VERSION
-from unittest_fakesession import FakeModuleCCSession
+from unittest_fakesession import FakeModuleCCSession, WouldBlockForever
 
 class TestHelperFunctions(unittest.TestCase):
     def test_parse_answer(self):
@@ -125,6 +125,8 @@
         self.assertTrue("Spec1" in fake_session.subscriptions)
 
         self.assertEqual(len(fake_session.message_queue), 0)
+        fake_session.group_sendmsg(None, 'Spec1')
+        fake_session.group_sendmsg(None, 'Spec1')
         self.assertRaises(ModuleCCSessionError, mccs.start)
         self.assertEqual(len(fake_session.message_queue), 2)
         self.assertEqual({'command': ['module_spec', {'module_name': 'Spec1'}]},
@@ -150,6 +152,8 @@
         fake_session = FakeModuleCCSession()
         mccs = self.create_session("spec2.spec", None, None, fake_session)
         self.assertEqual(len(fake_session.message_queue), 0)
+        fake_session.group_sendmsg(None, 'Spec2')
+        fake_session.group_sendmsg(None, 'Spec2')
         self.assertRaises(ModuleCCSessionError, mccs.start)
         self.assertEqual(len(fake_session.message_queue), 2)
         self.assertEqual({'command': ['module_spec', mccs.specification._module_spec]},
@@ -173,6 +177,8 @@
         mccs = self.create_session("spec2.spec", None, None, fake_session)
         mccs.set_config_handler(self.my_config_handler_ok)
         self.assertEqual(len(fake_session.message_queue), 0)
+        fake_session.group_sendmsg(None, 'Spec2')
+        fake_session.group_sendmsg(None, 'Spec2')
         self.assertRaises(ModuleCCSessionError, mccs.start)
         self.assertEqual(len(fake_session.message_queue), 2)
         self.assertEqual({'command': ['module_spec', mccs.specification._module_spec]},
@@ -196,6 +202,8 @@
         mccs = self.create_session("spec2.spec", None, None, fake_session)
         mccs.set_config_handler(self.my_config_handler_ok)
         self.assertEqual(len(fake_session.message_queue), 0)
+        fake_session.group_sendmsg(None, 'Spec2')
+        fake_session.group_sendmsg(None, 'Spec2')
         self.assertRaises(ModuleCCSessionError, mccs.start)
         self.assertEqual(len(fake_session.message_queue), 2)
         self.assertEqual({'command': ['module_spec', mccs.specification._module_spec]},
@@ -327,30 +335,173 @@
         self.assertEqual(len(fake_session.message_queue), 1)
         self.assertEqual({'result': [2, 'Spec2 has no command handler']},
                          fake_session.get_message('Spec2', None))
-        
-    def test_check_command7(self):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec2.spec", None, None, fake_session)
-        mccs.set_command_handler(self.my_command_handler_ok)
+
+    """Many check_command tests look too similar, this is common body."""
+    def common_check_command_check(self, cmd_handler,
+            cmd_check=lambda mccs, _: mccs.check_command()):
+        fake_session = FakeModuleCCSession()
+        mccs = self.create_session("spec2.spec", None, None, fake_session)
+        mccs.set_command_handler(cmd_handler)
         self.assertEqual(len(fake_session.message_queue), 0)
         cmd = isc.config.ccsession.create_command("print_message", "just a message")
         fake_session.group_sendmsg(cmd, 'Spec2')
         self.assertEqual(len(fake_session.message_queue), 1)
-        mccs.check_command()
+        cmd_check(mccs, fake_session)
+        return fake_session
+
+    def test_check_command7(self):
+        fake_session = self.common_check_command_check(
+            self.my_command_handler_ok)
         self.assertEqual(len(fake_session.message_queue), 1)
         self.assertEqual({'result': [0]},
                          fake_session.get_message('Spec2', None))
-        
+
     def test_check_command8(self):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec2.spec", None, None, fake_session)
-        mccs.set_command_handler(self.my_command_handler_no_answer)
+        fake_session = self.common_check_command_check(
+            self.my_command_handler_no_answer)
+        self.assertEqual(len(fake_session.message_queue), 0)
+
+    def test_check_command_block(self):
+        """See if the message gets there even in blocking mode."""
+        fake_session = self.common_check_command_check(
+            self.my_command_handler_ok,
+            lambda mccs, _: mccs.check_command(False))
+        self.assertEqual(len(fake_session.message_queue), 1)
+        self.assertEqual({'result': [0]},
+                         fake_session.get_message('Spec2', None))
+
+    def test_check_command_block_timeout(self):
+        """Check it works if session has timeout and it sets it back."""
+        def cmd_check(mccs, session):
+            session.set_timeout(1)
+            mccs.check_command(False)
+        fake_session = self.common_check_command_check(
+            self.my_command_handler_ok, cmd_check)
+        self.assertEqual(len(fake_session.message_queue), 1)
+        self.assertEqual({'result': [0]},
+                         fake_session.get_message('Spec2', None))
+        self.assertEqual(fake_session.get_timeout(), 1)
+
+    def test_check_command_blocks_forever(self):
+        """Check it would wait forever checking a command."""
+        fake_session = FakeModuleCCSession()
+        mccs = self.create_session("spec2.spec", None, None, fake_session)
+        mccs.set_command_handler(self.my_command_handler_ok)
+        self.assertRaises(WouldBlockForever, lambda: mccs.check_command(False))
+
+    def test_check_command_blocks_forever_timeout(self):
+        """Like above, but it should wait forever even with timeout here."""
+        fake_session = FakeModuleCCSession()
+        fake_session.set_timeout(1)
+        mccs = self.create_session("spec2.spec", None, None, fake_session)
+        mccs.set_command_handler(self.my_command_handler_ok)
+        self.assertRaises(WouldBlockForever, lambda: mccs.check_command(False))
+
+    def test_check_command_without_recvmsg1(self):
+        "copied from test_check_command2"
+        fake_session = FakeModuleCCSession()
+        mccs = self.create_session("spec1.spec", None, None, fake_session)
+        mccs.set_config_handler(self.my_config_handler_ok)
+        self.assertEqual(len(fake_session.message_queue), 0)
+        cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'Spec1': 'abcd' })
+        env = { 'group': 'Spec1', 'from':None };
+        mccs.check_command_without_recvmsg(cmd, env)
+        self.assertEqual(len(fake_session.message_queue), 1)
+        self.assertEqual({'result': [1, 'No config_data specification']},
+                         fake_session.get_message('Spec1', None))
+ 
+    def test_check_command_without_recvmsg2(self):
+        "copied from test_check_command3"
+        fake_session = FakeModuleCCSession()
+        mccs = self.create_session("spec2.spec", None, None, fake_session)
+        mccs.set_config_handler(self.my_config_handler_ok)
+        self.assertEqual(len(fake_session.message_queue), 0)
+        cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'Spec2': { 'item1': 2 }})
+        self.assertEqual(len(fake_session.message_queue), 0)
+        env = { 'group':'Spec2', 'from':None }
+        mccs.check_command_without_recvmsg(cmd, env)
+        self.assertEqual(len(fake_session.message_queue), 1)
+        self.assertEqual({'result': [0]},
+                          fake_session.get_message('Spec2', None))
+ 
+    def test_check_command_without_recvmsg3(self):
+        "copied from test_check_command7"
+        fake_session = FakeModuleCCSession()
+        mccs = self.create_session("spec2.spec", None, None, fake_session)
+        mccs.set_command_handler(self.my_command_handler_ok)
         self.assertEqual(len(fake_session.message_queue), 0)
         cmd = isc.config.ccsession.create_command("print_message", "just a message")
-        fake_session.group_sendmsg(cmd, 'Spec2')
-        self.assertEqual(len(fake_session.message_queue), 1)
-        mccs.check_command()
-        self.assertEqual(len(fake_session.message_queue), 0)
+        env = { 'group':'Spec2', 'from':None }
+        self.assertEqual(len(fake_session.message_queue), 0)
+        mccs.check_command_without_recvmsg(cmd, env)
+        self.assertEqual({'result': [0]},
+                         fake_session.get_message('Spec2', None))
+ 
+    def test_check_command_without_recvmsg_remote_module(self):
+        "copied from test_check_command3"
+        fake_session = FakeModuleCCSession()
+        mccs = self.create_session("spec1.spec", None, None, fake_session)
+        mccs.set_config_handler(self.my_config_handler_ok)
+        self.assertEqual(len(fake_session.message_queue), 0)
+
+        fake_session.group_sendmsg(None, 'Spec2')
+        rmodname = mccs.add_remote_config(self.spec_file("spec2.spec"))
+        print(fake_session.message_queue)
+        self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
+                         fake_session.get_message('ConfigManager', None))
+        self.assertEqual(len(fake_session.message_queue), 0)
+
+        cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'Spec2': { 'item1': 2 }})
+        env = { 'group':'Spec2', 'from':None }
+        self.assertEqual(len(fake_session.message_queue), 0)
+        mccs.check_command_without_recvmsg(cmd, env)
+        self.assertEqual(len(fake_session.message_queue), 0)
+ 
+    def test_check_command_without_recvmsg_remote_module2(self):
+        "copied from test_check_command3"
+        fake_session = FakeModuleCCSession()
+        mccs = self.create_session("spec1.spec", None, None, fake_session)
+        mccs.set_config_handler(self.my_config_handler_ok)
+        self.assertEqual(len(fake_session.message_queue), 0)
+
+        fake_session.group_sendmsg(None, 'Spec2')
+        rmodname = mccs.add_remote_config(self.spec_file("spec2.spec"))
+        self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
+                         fake_session.get_message('ConfigManager', None))
+        self.assertEqual(len(fake_session.message_queue), 0)
+
+        cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'Spec3': { 'item1': 2 }})
+        env = { 'group':'Spec3', 'from':None }
+        self.assertEqual(len(fake_session.message_queue), 0)
+        mccs.check_command_without_recvmsg(cmd, env)
+        self.assertEqual(len(fake_session.message_queue), 0)
+ 
+    def test_check_command_block_timeout(self):
+        """Check it works if session has timeout and it sets it back."""
+        def cmd_check(mccs, session):
+            session.set_timeout(1)
+            mccs.check_command(False)
+        fake_session = self.common_check_command_check(
+            self.my_command_handler_ok, cmd_check)
+        self.assertEqual(len(fake_session.message_queue), 1)
+        self.assertEqual({'result': [0]},
+                         fake_session.get_message('Spec2', None))
+        self.assertEqual(fake_session.get_timeout(), 1)
+
+    def test_check_command_blocks_forever(self):
+        """Check it would wait forever checking a command."""
+        fake_session = FakeModuleCCSession()
+        mccs = self.create_session("spec2.spec", None, None, fake_session)
+        mccs.set_command_handler(self.my_command_handler_ok)
+        self.assertRaises(WouldBlockForever, lambda: mccs.check_command(False))
+
+    def test_check_command_blocks_forever_timeout(self):
+        """Like above, but it should wait forever even with timeout here."""
+        fake_session = FakeModuleCCSession()
+        fake_session.set_timeout(1)
+        mccs = self.create_session("spec2.spec", None, None, fake_session)
+        mccs.set_command_handler(self.my_command_handler_ok)
+        self.assertRaises(WouldBlockForever, lambda: mccs.check_command(False))
 
     def test_remote_module(self):
         fake_session = FakeModuleCCSession()
@@ -360,6 +511,7 @@
         self.assertRaises(ModuleCCSessionError, mccs.get_remote_config_value, "Spec2", "item1")
 
         self.assertFalse("Spec2" in fake_session.subscriptions)
+        fake_session.group_sendmsg(None, 'Spec2')
         rmodname = mccs.add_remote_config(self.spec_file("spec2.spec"))
         self.assertTrue("Spec2" in fake_session.subscriptions)
         self.assertEqual("Spec2", rmodname)
@@ -373,6 +525,7 @@
         self.assertRaises(ModuleCCSessionError, mccs.get_remote_config_value, "Spec2", "item1")
 
         # test if unsubscription is alse sent when object is deleted
+        fake_session.group_sendmsg({'result' : [0]}, 'Spec2')
         rmodname = mccs.add_remote_config(self.spec_file("spec2.spec"))
         self.assertTrue("Spec2" in fake_session.subscriptions)
         mccs = None
@@ -383,6 +536,7 @@
         fake_session = FakeModuleCCSession()
         mccs = self.create_session("spec1.spec", None, None, fake_session)
         mccs.set_command_handler(self.my_command_handler_ok)
+        fake_session.group_sendmsg(None, 'Spec2')
         rmodname = mccs.add_remote_config(self.spec_file("spec2.spec"))
 
         # remove the 'get config' from the queue

Modified: branches/trac358/src/lib/python/isc/config/tests/cfgmgr_test.py
==============================================================================
--- branches/trac358/src/lib/python/isc/config/tests/cfgmgr_test.py (original)
+++ branches/trac358/src/lib/python/isc/config/tests/cfgmgr_test.py Thu Nov  4 06:21:08 2010
@@ -255,6 +255,7 @@
                          self.fake_session.get_message(self.name, None))
         self.assertEqual(len(self.fake_session.message_queue), 0)
 
+        self.fake_session.group_sendmsg(None, 'ConfigManager')
         self._handle_msg_helper({ "command": [ "set_config", [ ] ] },
                                 {'result': [1, 'Wrong number of arguments']} )
         self._handle_msg_helper({ "command": [ "set_config", [ self.name, { "test": 125 }] ] },

Modified: branches/trac358/src/lib/python/isc/config/tests/unittest_fakesession.py
==============================================================================
--- branches/trac358/src/lib/python/isc/config/tests/unittest_fakesession.py (original)
+++ branches/trac358/src/lib/python/isc/config/tests/unittest_fakesession.py Thu Nov  4 06:21:08 2010
@@ -16,6 +16,13 @@
 # $Id$
 
 import isc
+
+class WouldBlockForever(Exception):
+    """
+    This is thrown by the FakeModuleCCSession if it would need
+    to block forever for incoming message.
+    """
+    pass
 
 #
 # We can probably use a more general version of this
@@ -64,13 +71,18 @@
         if 'group' in env:
             self.message_queue.append([ env['group'], None, msg])
 
-    def group_recvmsg(self, blocking, seq = None):
+    def group_recvmsg(self, nonblock=True, seq = None):
         for qm in self.message_queue:
-            if qm[0] in self.subscriptions and (qm[1] == None or qm[1] in self.subscriptions[qm[0]]):
+            if qm[0] in self.subscriptions and (qm[1] == None or qm[1] in
+                self.subscriptions[qm[0]]):
                 self.message_queue.remove(qm)
                 return qm[2], {'group': qm[0], 'from': qm[1]}
         if self._timeout == 0:
-            return None, None
+            if nonblock:
+                return None, None
+            else:
+                raise WouldBlockForever(
+                    "Blocking read without timeout and no message ready")
         else:
             raise isc.cc.SessionTimeout("Timeout set but no data to "
                                  "return to group_recvmsg()")
@@ -88,3 +100,6 @@
 
     def set_timeout(self, timeout):
         self._timeout = timeout
+
+    def get_timeout(self):
+        return self._timeout

Modified: branches/trac358/src/lib/python/isc/datasrc/master.py
==============================================================================
--- branches/trac358/src/lib/python/isc/datasrc/master.py (original)
+++ branches/trac358/src/lib/python/isc/datasrc/master.py Thu Nov  4 06:21:08 2010
@@ -269,7 +269,7 @@
             if second[-1] == '.':
                 self.__origin = second
             elif not self.__origin:
-                raise MasterFileError("$ORIGIN is not absolute in record:%s" % s)
+                raise MasterFileError("$ORIGIN is not absolute in record: %s" % s)
             elif self.__origin != '.':
                 self.__origin = second + '.' + self.__origin
             else:

Modified: branches/trac358/src/lib/python/isc/log/log.py
==============================================================================
--- branches/trac358/src/lib/python/isc/log/log.py (original)
+++ branches/trac358/src/lib/python/isc/log/log.py Thu Nov  4 06:21:08 2010
@@ -19,6 +19,7 @@
 To use, simply 'import isc.log.log' and log away!
 """
 import os
+import sys
 import syslog
 import logging
 import logging.handlers
@@ -31,47 +32,73 @@
           'error' : logging.ERROR,
           'critical' : logging.CRITICAL}
 
-
 FORMATTER = logging.Formatter("%(name)s: %(levelname)s: %(message)s")
 TIME_FORMATTER = logging.Formatter("%(asctime)s.%(msecs)03d %(name)s: %(levelname)s: %(message)s",
                                    "%d-%b-%Y %H:%M:%S")
 
+def log_err(err_type, err_msg):
+    sys.stderr.write(err_type + ": " + "%s.\n" % str(err_msg)[str(err_msg).find(']')+1:])
+
+
 class NSFileLogHandler(logging.handlers.RotatingFileHandler):
     """RotatingFileHandler: replace RotatingFileHandler with a custom handler"""
 
     def __init__(self, filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=0):
-        dir = os.path.split(filename)
-        if not (os.path.exists(dir[0])):
-            os.makedirs(dir[0])
-        super(NSFileLogHandler, self).__init__(filename, mode, maxBytes,
+        abs_file_name = self._get_abs_file_path(filename)
+        """Create log directory beforehand, because the underlying logging framework won't 
+        create non-exsiting log directory on writing logs.
+        """
+        if not (os.path.exists(os.path.dirname(abs_file_name))):
+            os.makedirs(os.path.dirname(abs_file_name))
+        super(NSFileLogHandler, self).__init__(abs_file_name, mode, maxBytes,
                                                 backupCount, encoding, delay)
+
+    def handleError(self, record):
+        """Overwrite handleError to provide more user-friendly error messages"""
+        if logging.raiseExceptions:
+            ei = sys.exc_info()
+            if (ei[1]):
+                sys.stderr.write("[b10-logging] : " + str(ei[1]))
+
+    def _get_abs_file_path(self, file_name):
+        """ Get absolute file path"""
+        # For a bare filename, log_dir will be set the current directory.
+        if not os.path.dirname(file_name):
+            abs_file_dir = os.getcwd()
+        else:
+            abs_file_dir = os.path.abspath(os.path.dirname(file_name))
+        abs_file_name = os.path.join(abs_file_dir, os.path.basename(file_name))
+        return abs_file_name
 
     def shouldRollover(self, record):
         """Rewrite RotatingFileHandler.shouldRollover. 
        
         If the log file is deleted at runtime, a new file will be created.
         """
-        dfn = self.baseFilename                 
+        dfn = self.baseFilename
         if (self.stream) and (not os.path.exists(dfn)): #Does log file exist?
-            self.stream.close()
-            dir = os.path.split(dfn)
-            if not (os.path.exists(dir[0])): #Does log subdirectory exist?
-                os.makedirs(dir[0])
+            self.stream = None
+            """ Log directory may be deleted while bind10 running or updated with a
+             non-existing directory. Need to create log directory beforehand, because
+             the underlying logging framework won't create non-exsiting log directory
+             on writing logs.
+             """
+            if not (os.path.exists(os.path.dirname(dfn))): #Does log subdirectory exist?
+                os.makedirs(os.path.dirname(dfn))
             self.stream = self._open()
         return super(NSFileLogHandler, self).shouldRollover(record)
     
     def update_config(self, file_name, backup_count, max_bytes):
         """Update RotatingFileHandler configuration.
-
-        If the file path does not exist, we will use the old log file.
+        Changes will be picked up in the next call to shouldRollover().
+
         input:
             log file name
             max backup count
             predetermined log file size
         """
-        dir = os.path.split(file_name)
-        if (os.path.exists(dir[0])):
-            self.baseFilename = file_name
+        abs_file_name = self._get_abs_file_path(file_name)
+        self.baseFilename = abs_file_name 
         self.maxBytes = max_bytes
         self.backupCount = backup_count
 
@@ -162,8 +189,9 @@
             try:
                 self._file_handler = NSFileLogHandler(filename = log_file,
                                           maxBytes = max_bytes, backupCount = backup_count)
-            except IOError:
+            except (IOError, OSError) as e:
                 self._file_handler = None
+                log_err("[b10-logging] Add file handler fail", str(e))
                 return
             self._file_handler.setFormatter(TIME_FORMATTER)
             self.addHandler(self._file_handler)
@@ -244,6 +272,9 @@
         logger.log_message('info', "We have a %s", "mysterious problem").
         """
         logLevel = LEVELS.get(level, logging.NOTSET)
-        self.log(logLevel, msg, *args, **kwargs)
-
-
+        try:
+            self.log(logLevel, msg, *args, **kwargs)
+        except (TypeError, KeyError) as e:
+            sys.stderr.write("[b10-logging] Log message fail %s\n" % (str(e)))
+
+

Modified: branches/trac358/src/lib/python/isc/log/tests/log_test.py
==============================================================================
--- branches/trac358/src/lib/python/isc/log/tests/log_test.py (original)
+++ branches/trac358/src/lib/python/isc/log/tests/log_test.py Thu Nov  4 06:21:08 2010
@@ -20,6 +20,7 @@
 from isc.log.log import *
 import unittest
 import os
+import sys
 import tempfile
 
 
@@ -46,21 +47,48 @@
         self.handler.shouldRollover(record)
         self.assertTrue(os.path.exists(self.FILE_LOG1.name))
 
+    def test_get_absolute_file_path(self):
+        abs_file_name = self.handler._get_abs_file_path(self.FILE_LOG1.name)
+        self.assertEqual(abs_file_name, self.FILE_LOG1.name)
+        # test bare filename
+        file_name1 = "bind10.py"
+        abs_file_name = self.handler._get_abs_file_path(file_name1)
+        self.assertEqual(abs_file_name, os.path.join(os.getcwd(), file_name1))
+        # test relative path 
+        file_name2 = "./bind10.py"
+        abs_file_name = self.handler._get_abs_file_path(file_name2)
+        self.assertEqual(abs_file_name, os.path.join(os.getcwd(), os.path.basename(file_name2)))
+         
     def test_update_config(self):
         self.handler.update_config(self.FILE_LOG2.name, 3, 512)
         self.assertEqual(self.handler.baseFilename, self.FILE_LOG2.name)
         self.assertEqual(self.handler.maxBytes, 512)
         self.assertEqual(self.handler.backupCount, 3)
 
-        dir = os.path.split(self.FILE_LOG3.name)                                        
-        path = dir[0] + "path_not_exists"
-        update_file = os.path.join(path, dir[1])
-
-        if not os.path.exists(path):
-            self.handler.update_config(update_file, 4, 1024)
-            self.assertEqual(self.handler.baseFilename, self.FILE_LOG2.name)
-            self.assertEqual(self.handler.maxBytes, 1024)
-            self.assertEqual(self.handler.backupCount, 4)
+        # check the existence of new log file.
+        # emit() will call shouldRollover() to update the log file
+        if(os.path.exists(self.FILE_LOG2.name)):
+            os.remove(self.FILE_LOG2.name)
+        record = logging.LogRecord(None, None, "", 0, "rotate file handler", (), None, None)
+        self.handler.emit(record)
+        self.assertTrue(os.path.exists(self.FILE_LOG2.name))
+
+    def test_handle_Error(self):
+        if(os.path.exists(self.FILE_LOG3.name)):
+            os.remove(self.FILE_LOG3.name)
+        # redirect error message to file
+        savederr = sys.stderr
+        errfd = open(self.FILE_LOG3.name, 'w+')
+        sys.stderr = errfd
+        record = logging.LogRecord(None, None, "", 0, "record message", (), None, None)
+        try:
+            raise ValueError("ValueError")
+        except ValueError:
+            self.handler.handleError(record)
+
+        self.assertEqual("[b10-logging] : ValueError", errfd.read())
+        sys.stderr = savederr
+        errfd.close()
 
     def tearDown(self):
         self.handler.flush()
@@ -78,50 +106,46 @@
         self.assertEqual(sysLevel, syslog.LOG_ERR)
 
     def test_emit(self):
-        record = logging.LogRecord(None, None, "", 0, "syslog handler", (), None, None)
+        syslog_message = "bind10 syslog testing"
+        record = logging.LogRecord(None, None, "", 0, syslog_message, (), None, None)
         self.handler.emit(record)
-
 
 class TestLogging(unittest.TestCase):
     
     def setUp(self):
-        self.FILE_STREAM_LOG1= tempfile.NamedTemporaryFile(mode='w',
+        self.FILE_STREAM_LOG1 = tempfile.NamedTemporaryFile(mode='w',
                                                       prefix="b10",
                                                       delete=True)
-        self.FILE_STREAM_LOG2= tempfile.NamedTemporaryFile(mode='w',
+        self.FILE_STREAM_LOG2 = tempfile.NamedTemporaryFile(mode='w',
                                                       prefix="b10",
                                                       delete=True)
-        self.FILE_STREAM_LOG3= tempfile.NamedTemporaryFile(mode='w',
+        self.FILE_STREAM_LOG3 = tempfile.NamedTemporaryFile(mode='w',
                                                       prefix="b10",
                                                       delete=True)
         self.file_stream_logger = NSLogger('File_Stream_Logger',
                                            self.FILE_STREAM_LOG1.name,
                                            'debug', 5, 1024, True)
         self.syslog_logger = NSLogger('SysLogger', '', 'info', 5, 1024, False)
+        self.stderr_bak = sys.stderr
+        sys.stderr = open(os.devnull, 'w')
     
     def test_logging_init(self):
         self.assertNotEqual(self.file_stream_logger._file_handler, None)
         self.assertNotEqual(self.file_stream_logger._stream_handler, None)
         self.assertEqual(self.file_stream_logger._syslog_handler, None)
 
-        ret = self.file_stream_logger._file_handler in self.file_stream_logger.handlers
-        self.assertTrue(ret)
-        ret = self.file_stream_logger._stream_handler in self.file_stream_logger.handlers
-        self.assertTrue(ret)
-        ret = self.file_stream_logger._syslog_handler in self.file_stream_logger.handlers
-        self.assertFalse(ret)
+        self.assertIn(self.file_stream_logger._file_handler, self.file_stream_logger.handlers)
+        self.assertIn(self.file_stream_logger._stream_handler, self.file_stream_logger.handlers)
+        self.assertNotIn(self.file_stream_logger._syslog_handler, self.file_stream_logger.handlers)
         logLevel = LEVELS.get('debug', logging.NOTSET)
         self.assertEqual(self.file_stream_logger.getEffectiveLevel(), logLevel)
 
         self.assertEqual(self.syslog_logger._file_handler, None)
         self.assertEqual(self.syslog_logger._stream_handler, None)
         self.assertNotEqual(self.syslog_logger._syslog_handler, None)
-        ret = self.syslog_logger._file_handler in self.syslog_logger.handlers
-        self.assertFalse(ret)
-        ret = self.syslog_logger._stream_handler in self.syslog_logger.handlers
-        self.assertFalse(ret)
-        ret = self.syslog_logger._syslog_handler in self.syslog_logger.handlers
-        self.assertTrue(ret)
+        self.assertNotIn(self.syslog_logger._file_handler, self.syslog_logger.handlers)
+        self.assertNotIn(self.syslog_logger._stream_handler, self.syslog_logger.handlers)
+        self.assertIn(self.syslog_logger._syslog_handler, self.syslog_logger.handlers)
 
         logLevel = LEVELS.get('info', logging.NOTSET)
         self.assertEqual(self.syslog_logger.getEffectiveLevel(), logLevel)
@@ -131,41 +155,52 @@
             self.syslog_logger.removeHandler(self.syslog_logger._file_handler)
         
         self.syslog_logger._add_rotate_handler('', 5, 1024)
-        ret = self.syslog_logger._file_handler in self.syslog_logger.handlers
-        self.assertFalse(ret)
+        self.assertNotIn(self.syslog_logger._file_handler, self.syslog_logger.handlers)
 
         self.syslog_logger._add_rotate_handler(self.FILE_STREAM_LOG1.name, 5, 1024)
-        ret = self.syslog_logger._file_handler in self.syslog_logger.handlers
-        self.assertTrue(ret)
+        self.assertIn(self.syslog_logger._file_handler, self.syslog_logger.handlers)
+
+        # test IOError exception
+        self.syslog_logger.removeHandler(self.syslog_logger._file_handler)
+        log_file = self.FILE_STREAM_LOG1.name + '/logfile'
+        self.syslog_logger._add_rotate_handler(log_file, 5, 1024)
+        self.assertNotIn(self.syslog_logger._file_handler, self.syslog_logger.handlers)
 
     def test_add_stream_handler(self):
         if(self.file_stream_logger._stream_handler in self.file_stream_logger.handlers):
             self.file_stream_logger.removeHandler(self.file_stream_logger._stream_handler)
 
         self.file_stream_logger._add_stream_handler()
-        ret = self.file_stream_logger._stream_handler in self.file_stream_logger.handlers
-        self.assertTrue(ret)
+        self.assertIn(self.file_stream_logger._stream_handler, self.file_stream_logger.handlers)
 
     def test_add_syslog_handler(self):
         if(self.syslog_logger._syslog_handler in self.syslog_logger.handlers):
             self.syslog_logger.removeHandler(self.syslog_logger._syslog_handler)
 
         self.syslog_logger._add_syslog_handler()
-        ret = self.syslog_logger._syslog_handler in self.syslog_logger.handlers
-        self.assertTrue(ret)
+        self.assertIn(self.syslog_logger._syslog_handler, self.syslog_logger.handlers)
 
     def test_update_rotate_handler(self):
         self.file_stream_logger._update_rotate_handler(self.FILE_STREAM_LOG2.name, 4, 1024)
-        ret = self.file_stream_logger._file_handler in self.file_stream_logger.handlers
-        self.assertTrue(ret)
+        self.assertIn(self.file_stream_logger._file_handler, self.file_stream_logger.handlers)
 
         self.file_stream_logger._update_rotate_handler('', 5, 1024)
-        ret = self.file_stream_logger._file_handler in self.file_stream_logger.handlers
-        self.assertFalse(ret)
+        self.assertNotIn(self.file_stream_logger._file_handler, self.file_stream_logger.handlers)
 
         self.file_stream_logger._update_rotate_handler(self.FILE_STREAM_LOG1.name, 4, 1024)
-        ret = self.file_stream_logger._file_handler in self.file_stream_logger.handlers
-        self.assertTrue(ret)
+        self.assertIn(self.file_stream_logger._file_handler, self.file_stream_logger.handlers)
+
+    def test_get_config(self):
+        config_data = {'log_file' : self.FILE_STREAM_LOG1.name,
+                       'log_severity' : 'critical',
+                       'log_versions' : 4,
+                       'log_max_bytes' : 1024}
+        self.file_stream_logger._get_config(config_data)
+        self.assertEqual(self.file_stream_logger._log_file, self.FILE_STREAM_LOG1.name)
+        self.assertEqual(self.file_stream_logger._severity, 'critical')
+        self.assertEqual(self.file_stream_logger._versions, 4)
+        self.assertEqual(self.file_stream_logger._max_bytes, 1024)
+
 
     def test_update_config(self):
         update_config = {'log_file' : self.FILE_STREAM_LOG1.name,
@@ -183,15 +218,20 @@
                          'log_max_bytes' : 1024}
         self.file_stream_logger.update_config(update_config)
         self.file_stream_logger.log_message('debug', 'debug message')
-        self.file_stream_logger.log_message('info', 'info message')
         self.file_stream_logger.log_message('warning', 'warning message')
         self.file_stream_logger.log_message('error', 'error message')
+        #test non-exist log level
+        self.assertRaises(None, self.file_stream_logger.log_message('not-exist', 'not exist message'))
+        #test log_message KeyError exception
+        self.assertRaises(None, self.file_stream_logger.log_message('critical', 'critical message', extra=['message', 'asctime']))
         self.assertTrue(os.path.exists(self.FILE_STREAM_LOG3.name))
     
     def tearDown(self):
         self.FILE_STREAM_LOG1.close()
         self.FILE_STREAM_LOG2.close()
         self.FILE_STREAM_LOG3.close()
+        sys.stderr.flush();
+        sys.stderr = self.stderr_bak
 
 if __name__ == '__main__':
     unittest.main()

Modified: branches/trac358/src/lib/python/isc/notify/notify_out.py
==============================================================================
--- branches/trac358/src/lib/python/isc/notify/notify_out.py (original)
+++ branches/trac358/src/lib/python/isc/notify/notify_out.py Thu Nov  4 06:21:08 2010
@@ -19,6 +19,7 @@
 import socket
 import threading
 import time
+import errno
 from isc.datasrc import sqlite3_ds
 import isc
 try: 
@@ -44,29 +45,11 @@
 _BAD_QR = 4
 _BAD_REPLY_PACKET = 5
 
+SOCK_DATA = b's'
 def addr_to_str(addr):
     return '%s#%s' % (addr[0], addr[1])
 
-def dispatcher(notifier):
-    '''The loop function for handling notify related events.
-    If one zone get the notify reply before timeout, call the
-    handle to process the reply. If one zone can't get the notify
-    before timeout, call the handler to resend notify or notify 
-    next slave.  
-    notifier: one object of class NotifyOut. '''
-    while True:
-        replied_zones, not_replied_zones = notifier._wait_for_notify_reply()
-        if len(replied_zones) == 0 and len(not_replied_zones) == 0:
-            time.sleep(_IDLE_SLEEP_TIME) #TODO set a better time for idle sleep
-            continue
-
-        for name_ in replied_zones:
-            notifier._zone_notify_handler(replied_zones[name_], _EVENT_READ)
-            
-        for name_ in not_replied_zones:
-            if not_replied_zones[name_].notify_timeout <= time.time():
-                notifier._zone_notify_handler(not_replied_zones[name_], _EVENT_TIMEOUT)
- 
+
 class ZoneNotifyInfo:
     '''This class keeps track of notify-out information for one zone.'''
 
@@ -115,14 +98,17 @@
 
 class NotifyOut:
     '''This class is used to handle notify logic for all zones(sending
-    notify message to its slaves).The only interface provided to 
-    the user is send_notify(). the object of this class should be 
-    used together with function dispatcher(). '''
+    notify message to its slaves). notify service can be started by 
+    calling  dispatcher(), and it can be stoped by calling shutdown()
+    in another thread. ''' 
     def __init__(self, datasrc_file, log=None, verbose=True):
         self._notify_infos = {} # key is (zone_name, zone_class)
         self._waiting_zones = []
         self._notifying_zones = []
         self._log = log
+        self._serving = False
+        self._read_sock, self._write_sock = socket.socketpair()
+        self._read_sock.setblocking(False)
         self.notify_num = 0  # the count of in progress notifies
         self._verbose = verbose
         self._lock = threading.Lock()
@@ -164,6 +150,70 @@
                 self._notify_infos[zone_id].prepare_notify_out()
                 self.notify_num += 1 
                 self._notifying_zones.append(zone_id)
+
+    def _dispatcher(self, started_event):
+        started_event.set() # Let the master know we are alive already
+        while self._serving:
+            replied_zones, not_replied_zones = self._wait_for_notify_reply()
+
+            for name_ in replied_zones:
+                self._zone_notify_handler(replied_zones[name_], _EVENT_READ)
+
+            for name_ in not_replied_zones:
+                if not_replied_zones[name_].notify_timeout <= time.time():
+                    self._zone_notify_handler(not_replied_zones[name_], _EVENT_TIMEOUT)
+
+    def dispatcher(self, daemon=False):
+        """Spawns a thread that will handle notify related events.
+
+        If one zone get the notify reply before timeout, call the
+        handle to process the reply. If one zone can't get the notify
+        before timeout, call the handler to resend notify or notify 
+        next slave.  
+
+        The thread can be stopped by calling shutdown().
+
+        Returns the thread object to anyone interested.
+        """
+
+        if self._serving:
+            raise RuntimeError(
+                'Dispatcher already running, tried to start twice')
+
+        # Prepare for launch
+        self._serving = True
+        started_event = threading.Event()
+
+        # Start
+        self._thread = threading.Thread(target=self._dispatcher,
+            args=[started_event])
+        if daemon:
+            self._thread.daemon = daemon
+        self._thread.start()
+
+        # Wait for it to get started
+        started_event.wait()
+
+        # Return it to anyone listening
+        return self._thread
+
+    def shutdown(self):
+        """Stop the dispatcher() thread. Blocks until the thread stopped."""
+
+        if not self._serving:
+            raise RuntimeError('Tried to stop while not running')
+
+        # Ask it to stop
+        self._serving = False
+        self._write_sock.send(SOCK_DATA) # make self._read_sock be readable.
+
+        # Wait for it
+        self._thread.join()
+
+        # Clean up
+        self._write_sock = None
+        self._read_sock = None
+        self._thread = None
 
     def _get_rdata_data(self, rr):
         return rr[7].strip()
@@ -200,49 +250,68 @@
         return addr_list
 
     def _prepare_select_info(self):
-        '''Prepare the information for select(), returned 
-        value is one tuple 
+        '''
+        Prepare the information for select(), returned
+        value is one tuple
         (block_timeout, valid_socks, notifying_zones)
         block_timeout: the timeout for select()
         valid_socks: sockets list for waiting ready reading.
-        notifying_zones: the zones which have been triggered 
-                        for notify. '''
+        notifying_zones: the zones which have been triggered
+                        for notify.
+        '''
         valid_socks = []
         notifying_zones = {}
-        min_timeout = None 
+        min_timeout = None
         for info in self._notify_infos:
             sock = self._notify_infos[info].get_socket()
             if sock:
                 valid_socks.append(sock)
                 notifying_zones[info] = self._notify_infos[info]
                 tmp_timeout = self._notify_infos[info].notify_timeout
-                if min_timeout:
+                if min_timeout is not None:
                     if tmp_timeout < min_timeout:
                         min_timeout = tmp_timeout
                 else:
                     min_timeout = tmp_timeout
-       
-        block_timeout = 0
-        if min_timeout:
+
+        block_timeout = _IDLE_SLEEP_TIME
+        if min_timeout is not None:
             block_timeout = min_timeout - time.time()
             if block_timeout < 0:
                 block_timeout = 0
-        
+
         return (block_timeout, valid_socks, notifying_zones)
 
     def _wait_for_notify_reply(self):
-        '''receive notify replies in specified time. returned value 
-        is one tuple:(replied_zones, not_replied_zones)
+        '''
+        Receive notify replies in specified time. returned value
+        is one tuple:(replied_zones, not_replied_zones). ({}, {}) is
+        returned if shutdown() was called.
+
         replied_zones: the zones which receive notify reply.
         not_replied_zones: the zones which haven't got notify reply.
+
         '''
-        (block_timeout, valid_socks, notifying_zones) = self._prepare_select_info()
+        (block_timeout, valid_socks, notifying_zones) = \
+            self._prepare_select_info()
+        # This is None only during some tests
+        if self._read_sock is not None:
+            valid_socks.append(self._read_sock)
         try:
             r_fds, w, e = select.select(valid_socks, [], [], block_timeout)
         except select.error as err:
             if err.args[0] != EINTR:
-                return [], []
-        
+                return {}, {}
+
+        if self._read_sock in r_fds: # user has called shutdown()
+            try:
+                # Noone should write anything else than shutdown
+                assert self._read_sock.recv(len(SOCK_DATA)) == SOCK_DATA
+                return {}, {}
+            except socket.error as e: # Workaround around rare linux bug
+                if e.errno != errno.EAGAIN and e.errno != errno.EWOULDBLOCK:
+                    raise
+
         not_replied_zones = {}
         replied_zones = {}
         for info in notifying_zones:

Modified: branches/trac358/src/lib/python/isc/notify/tests/notify_out_test.py
==============================================================================
--- branches/trac358/src/lib/python/isc/notify/tests/notify_out_test.py (original)
+++ branches/trac358/src/lib/python/isc/notify/tests/notify_out_test.py Thu Nov  4 06:21:08 2010
@@ -20,7 +20,7 @@
 import time
 import socket
 from isc.datasrc import sqlite3_ds
-from isc.notify import notify_out
+from isc.notify import notify_out, SOCK_DATA
 
 class TestZoneNotifyInfo(unittest.TestCase):
     def setUp(self):
@@ -53,8 +53,6 @@
 
 class TestNotifyOut(unittest.TestCase):
     def setUp(self):
-        self.old_stdout = sys.stdout
-        sys.stdout = open(os.devnull, 'w')
         self._db_file = tempfile.NamedTemporaryFile(delete=False)
         sqlite3_ds.load(self._db_file.name, 'cn.', self._cn_data_reader)
         sqlite3_ds.load(self._db_file.name, 'com.', self._com_data_reader)
@@ -70,7 +68,6 @@
         info.notify_slaves.append(('1.1.1.1', 5353))
 
     def tearDown(self):
-        sys.stdout = self.old_stdout
         self._db_file.close()
         os.unlink(self._db_file.name)
 
@@ -123,6 +120,20 @@
         self.assertTrue(('com.', 'IN') in timeout_zones.keys())
         self.assertLess(time.time(), self._notify._notify_infos[('com.', 'IN')].notify_timeout)
     
+    def test_wait_for_notify_reply_2(self):
+        # Test the returned value when the read_side socket is readable.
+        self._notify.send_notify('cn.')
+        self._notify.send_notify('com.')
+
+        # Now make one socket be readable
+        self._notify._notify_infos[('cn.', 'IN')].notify_timeout = time.time() + 10
+        self._notify._notify_infos[('com.', 'IN')].notify_timeout = time.time() + 10
+        self._notify._read_sock, self._notify._write_sock = socket.socketpair()
+        self._notify._write_sock.send(SOCK_DATA)
+        replied_zones, timeout_zones = self._notify._wait_for_notify_reply()
+        self.assertEqual(0, len(replied_zones))
+        self.assertEqual(0, len(timeout_zones))
+
     def test_notify_next_target(self):
         self._notify.send_notify('cn.')
         self._notify.send_notify('com.')
@@ -258,7 +269,7 @@
         
     def test_prepare_select_info(self):
         timeout, valid_fds, notifying_zones = self._notify._prepare_select_info()
-        self.assertEqual(0, timeout)
+        self.assertEqual(notify_out._IDLE_SLEEP_TIME, timeout)
         self.assertListEqual([], valid_fds)
 
         self._notify._notify_infos[('cn.', 'IN')]._sock = 1
@@ -279,6 +290,12 @@
         self.assertEqual(timeout, 0)
         self.assertListEqual([2, 1], valid_fds)
 
+    def test_shutdown(self):
+        thread = self._notify.dispatcher()
+        self.assertTrue(thread.is_alive())
+        self._notify.shutdown()
+        self.assertFalse(thread.is_alive())
+
 if __name__== "__main__":
     unittest.main()
 

Modified: branches/trac358/src/lib/xfr/Makefile.am
==============================================================================
--- branches/trac358/src/lib/xfr/Makefile.am (original)
+++ branches/trac358/src/lib/xfr/Makefile.am Thu Nov  4 06:21:08 2010
@@ -1,8 +1,12 @@
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
+AM_CPPFLAGS += $(BOOST_INCLUDES)
 
 AM_CXXFLAGS = $(B10_CXXFLAGS) -Wno-strict-aliasing
 AM_CXXFLAGS += -Wno-unused-parameter # see src/lib/cc/Makefile.am
+if USE_CLANGPP
+AM_CXXFLAGS += -Wno-error
+endif
 
 CLEANFILES = *.gcno *.gcda
 

Modified: branches/trac358/src/lib/xfr/python_xfr.cc
==============================================================================
--- branches/trac358/src/lib/xfr/python_xfr.cc (original)
+++ branches/trac358/src/lib/xfr/python_xfr.cc Thu Nov  4 06:21:08 2010
@@ -28,8 +28,7 @@
 using namespace isc::xfr;
 using namespace boost::python;
 
-BOOST_PYTHON_MODULE(bind10_xfr)
-{
+BOOST_PYTHON_MODULE(bind10_xfr) {
     def("recv_fd", &recv_fd);
     def("send_fd", &send_fd);
 }

Modified: branches/trac358/src/lib/xfr/xfrout_client.cc
==============================================================================
--- branches/trac358/src/lib/xfr/xfrout_client.cc (original)
+++ branches/trac358/src/lib/xfr/xfrout_client.cc Thu Nov  4 06:21:08 2010
@@ -47,8 +47,7 @@
     impl_(new XfroutClientImpl(file))
 {}
 
-XfroutClient::~XfroutClient()
-{
+XfroutClient::~XfroutClient() {
     delete impl_;
 }
 




More information about the bind10-changes mailing list