[svn] commit: r1248 - in /branches/jinmei-asio: ./ src/bin/ src/bin/bind10/ src/bin/bind10/tests/ src/bin/cmdctl/ src/bin/loadzone/ src/bin/xfrin/ src/lib/auth/ src/lib/auth/testdata/ src/lib/cc/ src/lib/config/ src/lib/dns/ src/lib/dns/rdata/generic/ src/lib/python/isc/ src/lib/python/isc/Util/ src/lib/python/isc/auth/ src/lib/python/isc/cc/ src/lib/python/isc/config/

BIND 10 source code commits bind10-changes at lists.isc.org
Tue Mar 9 22:42:40 UTC 2010


Author: jinmei
Date: Tue Mar  9 22:42:40 2010
New Revision: 1248

Log:
sync with trunk

Added:
    branches/jinmei-asio/src/bin/bind10/tests/
      - copied from r1247, trunk/src/bin/bind10/tests/
    branches/jinmei-asio/src/bin/cmdctl/b10-cmdctl.xml
      - copied unchanged from r1247, trunk/src/bin/cmdctl/b10-cmdctl.xml
    branches/jinmei-asio/src/bin/loadzone/b10-loadzone.xml
      - copied unchanged from r1247, trunk/src/bin/loadzone/b10-loadzone.xml
    branches/jinmei-asio/src/bin/xfrin/
      - copied from r1247, trunk/src/bin/xfrin/
    branches/jinmei-asio/src/lib/auth/testdata/root.zone
      - copied unchanged from r1247, trunk/src/lib/auth/testdata/root.zone
    branches/jinmei-asio/src/lib/auth/testdata/test-root.sqlite3
      - copied unchanged from r1247, trunk/src/lib/auth/testdata/test-root.sqlite3
    branches/jinmei-asio/src/lib/dns/Makefile.dat
      - copied unchanged from r1247, trunk/src/lib/dns/Makefile.dat
    branches/jinmei-asio/src/lib/dns/message_python.cc
      - copied unchanged from r1247, trunk/src/lib/dns/message_python.cc
    branches/jinmei-asio/src/lib/dns/message_test.py
      - copied unchanged from r1247, trunk/src/lib/dns/message_test.py
    branches/jinmei-asio/src/lib/python/isc/auth/TODO
      - copied unchanged from r1247, trunk/src/lib/python/isc/auth/TODO
Removed:
    branches/jinmei-asio/src/bin/bind10/bind10_test.in
    branches/jinmei-asio/src/bin/bind10/bind10_test.py
Modified:
    branches/jinmei-asio/   (props changed)
    branches/jinmei-asio/configure.ac
    branches/jinmei-asio/src/bin/   (props changed)
    branches/jinmei-asio/src/bin/Makefile.am
    branches/jinmei-asio/src/bin/bind10/Makefile.am
    branches/jinmei-asio/src/bin/bind10/TODO
    branches/jinmei-asio/src/bin/bind10/bind10.py.in
    branches/jinmei-asio/src/bin/bind10/run_bind10.sh.in
    branches/jinmei-asio/src/bin/cmdctl/Makefile.am
    branches/jinmei-asio/src/bin/loadzone/b10-loadzone.py.in
    branches/jinmei-asio/src/lib/auth/data_source_sqlite3.cc
    branches/jinmei-asio/src/lib/auth/data_source_sqlite3.h
    branches/jinmei-asio/src/lib/auth/data_source_sqlite3_unittest.cc
    branches/jinmei-asio/src/lib/cc/   (props changed)
    branches/jinmei-asio/src/lib/config/ccsession.cc
    branches/jinmei-asio/src/lib/config/ccsession.h
    branches/jinmei-asio/src/lib/dns/   (props changed)
    branches/jinmei-asio/src/lib/dns/rdata/generic/rrsig_46.cc   (props changed)
    branches/jinmei-asio/src/lib/python/isc/Makefile.am
    branches/jinmei-asio/src/lib/python/isc/Util/Makefile.am
    branches/jinmei-asio/src/lib/python/isc/__init__.py
    branches/jinmei-asio/src/lib/python/isc/auth/Makefile.am
    branches/jinmei-asio/src/lib/python/isc/auth/master.py
    branches/jinmei-asio/src/lib/python/isc/auth/sqlite3_ds.py
    branches/jinmei-asio/src/lib/python/isc/cc/Makefile.am
    branches/jinmei-asio/src/lib/python/isc/cc/message.py
    branches/jinmei-asio/src/lib/python/isc/config/Makefile.am
    branches/jinmei-asio/src/lib/python/isc/config/ccsession.py
    branches/jinmei-asio/src/lib/python/isc/config/cfgmgr.py

Modified: branches/jinmei-asio/configure.ac
==============================================================================
--- branches/jinmei-asio/configure.ac (original)
+++ branches/jinmei-asio/configure.ac Tue Mar  9 22:42:40 2010
@@ -200,6 +200,7 @@
                  src/bin/loadzone/Makefile
                  src/bin/msgq/Makefile
                  src/bin/auth/Makefile
+                 src/bin/xfrin/Makefile
                  src/lib/Makefile
                  src/lib/cc/Makefile
                  src/lib/python/Makefile
@@ -218,8 +219,11 @@
            src/bin/cmdctl/cmdctl.py
            src/bin/cmdctl/run_b10-cmdctl.sh
            src/bin/cmdctl/unittest/cmdctl_test
+           src/bin/xfrin/unittest/xfrin_test
+           src/bin/xfrin/xfrin.py
+           src/bin/xfrin/run_b10-xfrin.sh
            src/bin/bind10/bind10.py
-           src/bin/bind10/bind10_test
+           src/bin/bind10/tests/bind10_test
            src/bin/bind10/run_bind10.sh
            src/bin/bindctl/bindctl
            src/bin/bindctl/unittest/bindctl_test
@@ -235,8 +239,10 @@
            src/lib/dns/tests/testdata/gen-wiredata.py
           ], [
            chmod +x src/bin/cmdctl/run_b10-cmdctl.sh
+           chmod +x src/bin/xfrin/run_b10-xfrin.sh
            chmod +x src/bin/bind10/run_bind10.sh
            chmod +x src/bin/cmdctl/unittest/cmdctl_test
+           chmod +x src/bin/xfrin/unittest/xfrin_test
            chmod +x src/bin/bindctl/unittest/bindctl_test
            chmod +x src/bin/bindctl/bindctl
            chmod +x src/bin/loadzone/run_loadzone

Modified: branches/jinmei-asio/src/bin/Makefile.am
==============================================================================
--- branches/jinmei-asio/src/bin/Makefile.am (original)
+++ branches/jinmei-asio/src/bin/Makefile.am Tue Mar  9 22:42:40 2010
@@ -1,1 +1,1 @@
-SUBDIRS = bind10 bindctl cfgmgr loadzone msgq host cmdctl auth
+SUBDIRS = bind10 bindctl cfgmgr loadzone msgq host cmdctl auth xfrin

Modified: branches/jinmei-asio/src/bin/bind10/Makefile.am
==============================================================================
--- branches/jinmei-asio/src/bin/bind10/Makefile.am (original)
+++ branches/jinmei-asio/src/bin/bind10/Makefile.am Tue Mar  9 22:42:40 2010
@@ -12,3 +12,6 @@
 	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
 	       -e "s|@@LIBEXECDIR@@|$(pkglibexecdir)|" bind10.py >$@
 	chmod a+x $@
+
+pytest:
+	$(SHELL) tests/bind10_test

Modified: branches/jinmei-asio/src/bin/bind10/TODO
==============================================================================
--- branches/jinmei-asio/src/bin/bind10/TODO (original)
+++ branches/jinmei-asio/src/bin/bind10/TODO Tue Mar  9 22:42:40 2010
@@ -8,7 +8,6 @@
   - Force-stop a component
 - Mechanism to wait for child to start before continuing
 - Way to ask a child to die politely 
-- Back-off mechanism for restarting failed processes
 - Start statistics daemon
 - Statistics interaction (?)
 - Use .spec file to define comands

Modified: branches/jinmei-asio/src/bin/bind10/bind10.py.in
==============================================================================
--- branches/jinmei-asio/src/bin/bind10/bind10.py.in (original)
+++ branches/jinmei-asio/src/bin/bind10/bind10.py.in Tue Mar  9 22:42:40 2010
@@ -2,6 +2,8 @@
 
 import sys; sys.path.append ('@@PYTHONPATH@@')
 import os
+import time
+import random
 
 """\
 This file implements the Boss of Bind (BoB, or bob) program.
@@ -50,10 +52,53 @@
 import isc
 
 # This is the version that gets displayed to the user.
-__version__ = "v20100225"
+__version__ = "v20100309"
 
 # Nothing at all to do with the 1990-12-10 article here:
 # http://www.subgenius.com/subg-digest/v2/0056.html
+
+class RestartSchedule:
+    """
+Keeps state when restarting something (in this case, a process).
+
+When a process dies unexpectedly, we need to restart it. However, if 
+it fails to restart for some reason, then we should not simply keep
+restarting it at high speed.
+
+A more sophisticated algorithm can be developed, but for now we choose
+a simple set of rules:
+
+  * If a process was been running for >=10 seconds, we restart it
+    right away.
+  * If a process was running for <10 seconds, we wait until 10 seconds
+    after it was started.
+
+To avoid programs getting into lockstep, we use a normal distribution
+to avoid being restarted at exactly 10 seconds."""
+
+    def __init__(self, restart_frequency=10.0):
+        self.restart_frequency = restart_frequency
+        self.run_start_time = None
+        self.run_stop_time = None
+        self.restart_time = None
+    
+    def set_run_start_time(self, when=None):
+        if when is None:
+            when = time.time()
+        self.run_start_time = when
+        sigma = self.restart_frequency * 0.05
+        self.restart_time = when + random.normalvariate(self.restart_frequency, 
+                                                        sigma)
+
+    def set_run_stop_time(self, when=None):
+        if when is None:
+            when = time.time()
+        self.run_stop_time = when
+
+    def get_restart_time(self, when=None):
+        if when is None:
+            when = time.time()
+        return max(when, self.restart_time)
 
 class ProcessInfo:
     """Information about a process"""
@@ -82,12 +127,14 @@
                                         close_fds=True,
                                         env=spawn_env,)
         self.pid = self.process.pid
+        self.restart_schedule.set_run_start_time()
 
     def __init__(self, name, args, env={}, dev_null_stdout=False):
         self.name = name 
         self.args = args
         self.env = env
         self.dev_null_stdout = dev_null_stdout
+        self.restart_schedule = RestartSchedule()
         self._spawn()
 
     def respawn(self):
@@ -229,6 +276,20 @@
         if self.verbose:
             sys.stdout.write("Started b10-auth (PID %d)\n" % auth.pid)
 
+        # start the b10-xfrin
+        if self.verbose:
+            sys.stdout.write("Starting b10-xfrin\n")
+        try:
+            xfrind = ProcessInfo("b10-xfrin", ['b10-xfrin'])
+        except Exception as e:
+            c_channel.process.kill()
+            bind_cfgd.process.kill()
+            auth.process.kill()
+            return "Unable to start b10-xfrin; " + str(e)
+        self.processes[xfrind.pid] = xfrind
+        if self.verbose:
+            sys.stdout.write("Started b10-xfrin (PID %d)\n" % xfrind.pid)
+
         # start the b10-cmdctl
         # XXX: we hardcode port 8080
         if self.verbose:
@@ -239,6 +300,7 @@
             c_channel.process.kill()
             bind_cfgd.process.kill()
             auth.process.kill()
+            xfrind.process.kill()
             return "Unable to start b10-cmdctl; " + str(e)
         self.processes[cmd_ctrld.pid] = cmd_ctrld
         if self.verbose:
@@ -317,6 +379,7 @@
             if pid == 0: break
             if pid in self.processes:
                 proc_info = self.processes.pop(pid)
+                proc_info.restart_schedule.set_run_stop_time()
                 self.dead_processes[proc_info.pid] = proc_info
                 if self.verbose:
                     sys.stdout.write("Process %s (PID %d) died.\n" % 
@@ -386,26 +449,39 @@
 
     def restart_processes(self):
         """Restart any dead processes."""
-        # XXX: this needs a back-off algorithm
+        next_restart = None
         # if we're shutting down, then don't restart
         if not self.runnable:
-            return
+            return next_restart
         # otherwise look through each dead process and try to restart
         still_dead = {}
+        now = time.time()
         for proc_info in self.dead_processes.values():
-            if self.verbose:
-                sys.stdout.write("Resurrecting dead %s process...\n" % 
-                                 proc_info.name)
-            try:
-                proc_info.respawn()
-                self.processes[proc_info.pid] = proc_info
+            restart_time = proc_info.restart_schedule.get_restart_time(now)
+            if restart_time > now:
+#                if self.verbose:
+#                    sys.stdout.write("Dead %s process waiting %.1f seconds "\
+#                                     "for resurrection\n" % 
+#                                     (proc_info.name, (restart_time-now)))
+                if (next_restart is None) or (next_restart > restart_time):
+                    next_restart = restart_time
+                still_dead[proc_info.pid] = proc_info
+            else:
                 if self.verbose:
-                    sys.stdout.write("Resurrected %s (PID %d)\n" %
-                                     (proc_info.name, proc_info.pid))
-            except:
-                still_dead[proc_info.pid] = proc_info
+                    sys.stdout.write("Resurrecting dead %s process...\n" % 
+                                     proc_info.name)
+                try:
+                    proc_info.respawn()
+                    self.processes[proc_info.pid] = proc_info
+                    if self.verbose:
+                        sys.stdout.write("Resurrected %s (PID %d)\n" %
+                                         (proc_info.name, proc_info.pid))
+                except:
+                    still_dead[proc_info.pid] = proc_info
         # remember any processes that refuse to be resurrected
         self.dead_processes = still_dead
+        # return the time when the next process is ready to be restarted
+        return next_restart
 
 def reaper(signal_number, stack_frame):
     """A child process has died (SIGCHLD received)."""
@@ -484,15 +560,18 @@
     while boss_of_bind.runnable:
         # clean up any processes that exited
         boss_of_bind.reap_children()
-        boss_of_bind.restart_processes()
-
-        # XXX: get time for next restart for timeout
+        next_restart = boss_of_bind.restart_processes()
+        if next_restart is None:
+            wait_time = None
+        else:
+            wait_time = max(next_restart - time.time(), 0)
 
         # select() can raise EINTR when a signal arrives, 
         # even if they are resumable, so we have to catch
         # the exception
         try:
-            (rlist, wlist, xlist) = select.select([wakeup_fd, ccs_fd], [], [])
+            (rlist, wlist, xlist) = select.select([wakeup_fd, ccs_fd], [], [], 
+                                                  wait_time)
         except select.error as err:
             if err.args[0] == errno.EINTR:
                 (rlist, wlist, xlist) = ([], [], [])

Modified: branches/jinmei-asio/src/bin/bind10/run_bind10.sh.in
==============================================================================
--- branches/jinmei-asio/src/bin/bind10/run_bind10.sh.in (original)
+++ branches/jinmei-asio/src/bin/bind10/run_bind10.sh.in Tue Mar  9 22:42:40 2010
@@ -5,7 +5,7 @@
 
 BIND10_PATH=@abs_top_builddir@/src/bin/bind10
 
-PATH=@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:$PATH
+PATH=@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/xfrin:$PATH
 export PATH
 
 PYTHONPATH=@abs_top_builddir@/src/lib/python

Modified: branches/jinmei-asio/src/bin/cmdctl/Makefile.am
==============================================================================
--- branches/jinmei-asio/src/bin/cmdctl/Makefile.am (original)
+++ branches/jinmei-asio/src/bin/cmdctl/Makefile.am Tue Mar  9 22:42:40 2010
@@ -3,12 +3,17 @@
 pkglibexec_SCRIPTS = b10-cmdctl
 
 b10_cmdctldir = $(DESTDIR)$(pkgdatadir)
+# TODO: this is dangerous -- will overwrite!
 b10_cmdctl_DATA = passwd.csv b10-cmdctl.pem
 
-CLEANFILES=	cmdctl.py
+CLEANFILES=	b10-cmdctl
 
 # TODO: does this need $$(DESTDIR) also?
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 b10-cmdctl: cmdctl.py
 	$(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" cmdctl.py >$@
 	chmod a+x $@
+
+install-data-local:
+	chmod go-rwx $(DESTDIR)$(pkgdatadir)/passwd.csv
+	chmod go-rwx $(DESTDIR)$(pkgdatadir)/b10-cmdctl.pem

Modified: branches/jinmei-asio/src/bin/loadzone/b10-loadzone.py.in
==============================================================================
--- branches/jinmei-asio/src/bin/loadzone/b10-loadzone.py.in (original)
+++ branches/jinmei-asio/src/bin/loadzone/b10-loadzone.py.in Tue Mar  9 22:42:40 2010
@@ -40,11 +40,13 @@
         exit(2)
 
     dbfile = '/tmp/zone.sqlite3'
-    initial_origin = '.'
+    initial_origin = ''
     for o, a in opts:
         if o in ("-d", "--dbfile"):
             dbfile = a
         elif o in ("-o", "--origin"):
+            if a[-1] != '.':
+                a += '.'
             initial_origin = a
         elif o in ("-h", "--help"):
             usage()
@@ -68,7 +70,6 @@
         exit(1)
 
     try:
-
         isc.auth.sqlite3_ds.load(dbfile, zone, isc.auth.master.zonedata, zf)
     except Exception as e:
         print("Error loading database: " + str(e))

Modified: branches/jinmei-asio/src/lib/auth/data_source_sqlite3.cc
==============================================================================
--- branches/jinmei-asio/src/lib/auth/data_source_sqlite3.cc (original)
+++ branches/jinmei-asio/src/lib/auth/data_source_sqlite3.cc Tue Mar  9 22:42:40 2010
@@ -185,10 +185,15 @@
                             RRsetList& target, const Name* zonename,
                             const Mode mode, uint32_t& flags) const
 {
-    const string s_name = name.toText();
-    const char* const c_name = s_name.c_str();
+    flags = 0;
+    int zone_id = (zonename == NULL) ? findClosest(name, NULL) :
+        findClosest(*zonename, NULL);
+    if (zone_id < 0) {
+        flags = NO_SUCH_ZONE;
+        return (0);
+    }
+
     sqlite3_stmt* query;
-
     switch (mode) {
     case ADDRESS:
         query = q_addrs;
@@ -204,15 +209,6 @@
         }
         break;
     }
-
-    flags = 0;
-
-    int zone_id = (zonename == NULL) ? findClosest(c_name, NULL) :
-        findClosest(zonename->toText().c_str(), NULL);
-    if (zone_id < 0) {
-        flags = NO_SUCH_ZONE;
-        return (0);
-    }
     
     sqlite3_reset(query);
     sqlite3_clear_bindings(query);
@@ -222,7 +218,8 @@
     if (rc != SQLITE_OK) {
         throw("Could not bind 1 (query)");
     }
-    rc = sqlite3_bind_text(query, 2, c_name, -1, SQLITE_STATIC);
+    const string s_name = name.toText();
+    rc = sqlite3_bind_text(query, 2, s_name.c_str(), -1, SQLITE_STATIC);
     if (rc != SQLITE_OK) {
         throw("Could not bind 2 (query)");
     }
@@ -282,29 +279,22 @@
 //  longest match found.
 //
 int
-Sqlite3DataSrc::findClosest(const char* const name,
-                            const char** position) const
-{
-    const char* current = name;
-
-    while (*current != 0) {
-        const int rc = hasExactZone(current);
+Sqlite3DataSrc::findClosest(const Name& name, unsigned int* position) const {
+    const unsigned int nlabels = name.getLabelCount();
+    for (unsigned int i = 0; i < nlabels; ++i) {
+        Name matchname(name.split(i, nlabels - i));
+        const int rc = hasExactZone(matchname.toText().c_str());
         if (rc >= 0) {
             if (position != NULL) {
-                *position = current;
+                *position = i;
             }
             return (rc);
         }
-        while (*current != '.' && *current != 0) {
-            ++current;
-        }
-        if (*current == '.') {
-            ++current;
-        }
     }
 
     return (-1);
 }
+
 
 void
 Sqlite3DataSrc::loadVersion(void) {
@@ -498,18 +488,20 @@
 
 void
 Sqlite3DataSrc::findClosestEnclosure(NameMatch& match,
-                                     const RRClass& qclass) const {
-    const char* position = NULL;
-    
+                                     const RRClass& qclass) const
+{
     if (qclass != getClass()) {
         return;
     }
 
-    if (findClosest(match.qname().toText().c_str(), &position) == -1) {
+    unsigned int position;
+    if (findClosest(match.qname(), &position) == -1) {
         return;
     }
 
-    match.update(*this, Name(position));
+    match.update(*this, match.qname().split(position,
+                                            match.qname().getLabelCount() -
+                                            position));
 }
 
 DataSrc::Result
@@ -519,8 +511,8 @@
                                  const Name* zonename) const
 {
     int zone_id = (zonename == NULL) ?
-        findClosest(qname.toText().c_str(), NULL) :
-        findClosest(zonename->toText().c_str(), NULL);
+        findClosest(qname, NULL) :
+        findClosest(*zonename, NULL);
 
     if (zone_id < 0) {
         return (ERROR);
@@ -557,7 +549,7 @@
                                   string& hashstr,
                                   RRsetList& target) const
 {
-    int zone_id = findClosest(zonename.toText().c_str(), NULL);
+    int zone_id = findClosest(zonename, NULL);
     if (zone_id < 0) {
         return (ERROR);
     }

Modified: branches/jinmei-asio/src/lib/auth/data_source_sqlite3.h
==============================================================================
--- branches/jinmei-asio/src/lib/auth/data_source_sqlite3.h (original)
+++ branches/jinmei-asio/src/lib/auth/data_source_sqlite3.h Tue Mar  9 22:42:40 2010
@@ -121,7 +121,7 @@
     int findRecords(const isc::dns::Name& name, const isc::dns::RRType& rdtype,
                     isc::dns::RRsetList& target, const isc::dns::Name* zonename,
                     const Mode mode, uint32_t& flags) const;
-    int findClosest(const char *name, const char **position) const;
+    int findClosest(const isc::dns::Name& name, unsigned int* position) const;
     void loadVersion(void);
     void setupPreparedStatements(void);
     void execSetupQuery(const char *query);

Modified: branches/jinmei-asio/src/lib/auth/data_source_sqlite3_unittest.cc
==============================================================================
--- branches/jinmei-asio/src/lib/auth/data_source_sqlite3_unittest.cc (original)
+++ branches/jinmei-asio/src/lib/auth/data_source_sqlite3_unittest.cc Tue Mar  9 22:42:40 2010
@@ -46,7 +46,9 @@
                      "{ \"database_file\": \"testdata/test.sqlite3\"}");
 static ElementPtr SQLITE_DBFILE_EXAMPLE2 = Element::createFromString(
                      "{ \"database_file\": \"testdata/test2.sqlite3\"}");
-// The following file must be non existent and mutt be "creatable";
+static ElementPtr SQLITE_DBFILE_EXAMPLE_ROOT = Element::createFromString(
+                     "{ \"database_file\": \"testdata/test-root.sqlite3\"}");
+// 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.
@@ -342,9 +344,9 @@
     answers.push_back(&expected_data);
     signatures.push_back(expected_sig_data);
 
-    checkFind(mode, data_source, query, expected_name, zone_name, expected_class,
-              expected_type, ttls, expected_flags, types, answers,
-              signatures);
+    checkFind(mode, data_source, query, expected_name, zone_name,
+              expected_class, expected_type, ttls, expected_flags, types,
+              answers, signatures);
 }
 
 TEST_F(Sqlite3DataSourceTest, close) {
@@ -355,11 +357,10 @@
     // Replace the data with a totally different zone.  This should succeed,
     // and shouldn't match any names in the previously managed domains.
     EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
-
     EXPECT_EQ(DataSrc::SUCCESS, data_source.init(SQLITE_DBFILE_EXAMPLE2));
 
     NameMatch name_match(www_name);
-    data_source.findClosestEnclosure(name_match, RRClass::IN());
+    data_source.findClosestEnclosure(name_match, rrclass);
     EXPECT_EQ(NULL, name_match.closestName());
     EXPECT_EQ(NULL, name_match.bestDataSrc());
 }
@@ -371,8 +372,18 @@
 
 TEST_F(Sqlite3DataSourceTest, findClosestEnclosure) {
     NameMatch name_match(www_name);
-    data_source.findClosestEnclosure(name_match, RRClass::IN());
+    data_source.findClosestEnclosure(name_match, rrclass);
     EXPECT_EQ(zone_name, *name_match.closestName());
+    EXPECT_EQ(&data_source, name_match.bestDataSrc());
+}
+
+TEST_F(Sqlite3DataSourceTest, findClosestEnclosureMatchRoot) {
+    EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
+    EXPECT_EQ(DataSrc::SUCCESS, data_source.init(SQLITE_DBFILE_EXAMPLE_ROOT));
+
+    NameMatch name_match(Name("org."));
+    data_source.findClosestEnclosure(name_match, rrclass);
+    EXPECT_EQ(Name("."), *name_match.closestName());
     EXPECT_EQ(&data_source, name_match.bestDataSrc());
 }
 
@@ -380,14 +391,14 @@
     // The search name exists both in the parent and child zones, but
     // child has a better match.
     NameMatch name_match(child_name);
-    data_source.findClosestEnclosure(name_match, RRClass::IN());
+    data_source.findClosestEnclosure(name_match, rrclass);
     EXPECT_EQ(child_name, *name_match.closestName());
     EXPECT_EQ(&data_source, name_match.bestDataSrc());
 }
 
 TEST_F(Sqlite3DataSourceTest, findClosestEnclosureNoMatch) {
     NameMatch name_match(nomatch_name);
-    data_source.findClosestEnclosure(name_match, RRClass::IN());
+    data_source.findClosestEnclosure(name_match, rrclass);
     EXPECT_EQ(NULL, name_match.closestName());
     EXPECT_EQ(NULL, name_match.bestDataSrc());
 }
@@ -696,7 +707,7 @@
               data_source.findCoveringNSEC3(*query, nsec3_zonename,
                                             hashstr, result_sets));
     RRsetList::iterator it = result_sets.begin();
-    checkRRset(*it, Name(hashstr).concatenate(nsec3_zonename), RRClass::IN(),
+    checkRRset(*it, Name(hashstr).concatenate(nsec3_zonename), rrclass,
                RRType::NSEC3(), RRTTL(7200), nsec3_data, &nsec3_sig_data);
     ++it;
     EXPECT_TRUE(it == result_sets.end());

Modified: branches/jinmei-asio/src/lib/config/ccsession.cc
==============================================================================
--- branches/jinmei-asio/src/lib/config/ccsession.cc (original)
+++ branches/jinmei-asio/src/lib/config/ccsession.cc Tue Mar  9 22:42:40 2010
@@ -42,7 +42,6 @@
 #include <cc/session.h>
 #include <exceptions/exceptions.h>
 
-//#include "common.h"
 #include "ccsession.h"
 #include "config.h"
 
@@ -143,10 +142,11 @@
     return "";
 }
 
-void
+ModuleSpec
 ModuleCCSession::read_module_specification(const std::string& filename) {
     std::ifstream file;
-
+    ModuleSpec module_spec;
+    
     // this file should be declared in a @something@ directive
     file.open(filename.c_str());
     if (!file) {
@@ -155,7 +155,7 @@
     }
 
     try {
-        module_specification_ = moduleSpecFromFile(file, true);
+        module_spec = moduleSpecFromFile(file, true);
     } catch (ParseError pe) {
         cout << "Error parsing module specification file: " << pe.what() << endl;
         exit(1);
@@ -164,6 +164,7 @@
         exit(1);
     }
     file.close();
+    return module_spec;
 }
 
 #ifdef HAVE_BOOSTLIB
@@ -211,7 +212,7 @@
         const std::string& command, const isc::data::ElementPtr args)
     ) throw (isc::cc::SessionError)
 {
-    read_module_specification(spec_file_name);
+    module_specification_ = read_module_specification(spec_file_name);
     sleep(1);
 
     module_name_ = module_specification_.getFullSpec()->get("module_name")->stringValue();
@@ -295,6 +296,7 @@
 {
     ElementPtr cmd, routing, data;
     if (session_.group_recvmsg(routing, data, true)) {
+        
         /* ignore result messages (in case we're out of sync, to prevent
          * pingpongs */
         if (!data->getType() == Element::map || data->contains("result")) {
@@ -304,7 +306,16 @@
         std::string cmd_str = parseCommand(arg, data);
         ElementPtr answer;
         if (cmd_str == "config_update") {
-            answer = handleConfigUpdate(arg);
+            std::string target_module = routing->get("group")->stringValue();
+            if (target_module == module_name_) {
+                answer = handleConfigUpdate(arg);
+            } else {
+                // ok this update is not for us, if we have this module
+                // in our remote config list, update that
+                updateRemoteConfig(target_module, arg);
+                // we're not supposed to answer to this, so return
+                return 0;
+            }
         } else {
             if (command_handler_) {
                 answer = command_handler_(cmd_str, arg);
@@ -318,5 +329,69 @@
     return 0;
 }
 
-}
-}
+std::string
+ModuleCCSession::addRemoteConfig(const std::string& spec_file_name)
+{
+    ModuleSpec rmod_spec = read_module_specification(spec_file_name);
+    std::string module_name = rmod_spec.getFullSpec()->get("module_name")->stringValue();
+    ConfigData rmod_config = ConfigData(rmod_spec);
+    session_.subscribe(module_name);
+
+    // Get the current configuration values for that module
+    ElementPtr cmd = Element::createFromString("{ \"command\": [\"get_config\", {\"module_name\":\"" + module_name + "\"} ] }");
+    ElementPtr env, answer;
+    int rcode;
+    
+    session_.group_sendmsg(cmd, "ConfigManager");
+    session_.group_recvmsg(env, answer, false);
+    ElementPtr new_config = parseAnswer(rcode, answer);
+    if (rcode == 0) {
+        rmod_config.setLocalConfig(new_config);
+    } else {
+        isc_throw(CCSessionError, "Error getting config for " + module_name + ": " + answer->str());
+    }
+
+    // all ok, add it
+    remote_module_configs_[module_name] = rmod_config;
+    return module_name;
+}
+
+void
+ModuleCCSession::removeRemoteConfig(const std::string& module_name)
+{
+    std::map<std::string, ConfigData>::iterator it;
+
+    it = remote_module_configs_.find(module_name);
+    if (it != remote_module_configs_.end()) {
+        remote_module_configs_.erase(it);
+        session_.unsubscribe(module_name);
+    }
+}
+
+ElementPtr
+ModuleCCSession::getRemoteConfigValue(const std::string& module_name, const std::string& identifier)
+{
+    std::map<std::string, ConfigData>::iterator it;
+
+    it = remote_module_configs_.find(module_name);
+    if (it != remote_module_configs_.end()) {
+        return remote_module_configs_[module_name].getValue(identifier);
+    } else {
+        isc_throw(CCSessionError, "Remote module " + module_name + " not found.");
+    }
+}
+
+void
+ModuleCCSession::updateRemoteConfig(const std::string& module_name, ElementPtr new_config)
+{
+    std::map<std::string, ConfigData>::iterator it;
+
+    it = remote_module_configs_.find(module_name);
+    if (it != remote_module_configs_.end()) {
+        ElementPtr rconf = (*it).second.getLocalConfig();
+        isc::data::merge(rconf, new_config);
+    }
+}
+
+}
+}

Modified: branches/jinmei-asio/src/lib/config/ccsession.h
==============================================================================
--- branches/jinmei-asio/src/lib/config/ccsession.h (original)
+++ branches/jinmei-asio/src/lib/config/ccsession.h Tue Mar  9 22:42:40 2010
@@ -101,6 +101,44 @@
      */
     void set_command_handler(isc::data::ElementPtr(*command_handler)(const std::string& command, const isc::data::ElementPtr args)) { command_handler_ = command_handler; };
 
+    /**
+     * Gives access to the configuration values of a different module
+     * Once this function has been called with the name of the specification
+     * file of the module you want the configuration of, you can use
+     * \c getRemoteConfigValue() to get a specific setting.
+     * Changes are automatically updated, but you cannot specify handlers
+     * for those changes, must use \c getRemoteConfigValue() to get a value
+     * This function will subscribe to the relevant module channel.
+     *
+     * \param spec_file_name The path to the specification file of
+     *                       the module we want to have configuration
+     *                       values from
+     * \return The name of the module specified in the given specification
+     *         file
+     */
+    std::string addRemoteConfig(const std::string& spec_file_name);
+
+    /**
+     * Removes the module with the given name from the remote config
+     * settings. If the module was not added with \c addRemoteConfig(),
+     * nothing happens.
+     */
+    void removeRemoteConfig(const std::string& module_name);
+
+    /**
+     * Returns the current configuration value for the given module
+     * name at the given identifier. See \c ConfigData::getValue() for
+     * more details.
+     * Raises a ModuleCCSessionError if the module name is unknown
+     * Raises a DataNotFoundError if the identifier does not exist
+     * in the specification.
+     *
+     * \param module_name The name of the module to get a config value for
+     * \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);
+    
 private:
     void init(
         std::string spec_file_name,
@@ -109,7 +147,7 @@
         isc::data::ElementPtr(*command_handler)(
             const std::string& command, const isc::data::ElementPtr args)
         ) throw (isc::cc::SessionError);
-    void read_module_specification(const std::string& filename);
+    ModuleSpec read_module_specification(const std::string& filename);
     void startCheck();
     
     std::string module_name_;
@@ -120,6 +158,9 @@
 
     isc::data::ElementPtr(*config_handler_)(isc::data::ElementPtr new_config);
     isc::data::ElementPtr(*command_handler_)(const std::string& command, const isc::data::ElementPtr args);
+
+    std::map<std::string, ConfigData> remote_module_configs_;
+    void updateRemoteConfig(const std::string& module_name, ElementPtr new_config);
 };
 
 ElementPtr createAnswer(const int rcode);

Modified: branches/jinmei-asio/src/lib/python/isc/Makefile.am
==============================================================================
--- branches/jinmei-asio/src/lib/python/isc/Makefile.am (original)
+++ branches/jinmei-asio/src/lib/python/isc/Makefile.am Tue Mar  9 22:42:40 2010
@@ -1,8 +1,5 @@
-SUBDIRS = auth cc Util config
+SUBDIRS = auth cc config # Util
 
-PY_MODULES=	__init__.py
+python_PYTHON = __init__.py
 
-install-data-local:
-	$(mkinstalldirs) $(DESTDIR)$(pyexecdir)/isc
-	@(for _foo_ in $(PY_MODULES) ; \
-		do $(INSTALL) -m 0644 $(top_srcdir)/src/lib/python/isc/$$_foo_ $(DESTDIR)$(pyexecdir)/isc/ ; done)
+pythondir = $(pyexecdir)/isc

Modified: branches/jinmei-asio/src/lib/python/isc/Util/Makefile.am
==============================================================================
--- branches/jinmei-asio/src/lib/python/isc/Util/Makefile.am (original)
+++ branches/jinmei-asio/src/lib/python/isc/Util/Makefile.am Tue Mar  9 22:42:40 2010
@@ -1,6 +1,3 @@
-PY_MODULES=	__init__.py hexdump.py
+python_PYTHON = __init__.py hexdump.py
 
-install-data-local:
-	$(mkinstalldirs) $(DESTDIR)$(pyexecdir)/isc/Util
-	@(for _foo_ in $(PY_MODULES) ; \
-		do $(INSTALL) -m 0644 $(top_srcdir)/src/lib/python/isc/Util/$$_foo_ $(DESTDIR)$(pyexecdir)/isc/Util/ ; done)
+pythondir = $(pyexecdir)/isc/Util

Modified: branches/jinmei-asio/src/lib/python/isc/__init__.py
==============================================================================
--- branches/jinmei-asio/src/lib/python/isc/__init__.py (original)
+++ branches/jinmei-asio/src/lib/python/isc/__init__.py Tue Mar  9 22:42:40 2010
@@ -1,4 +1,3 @@
 import isc.auth
 import isc.cc
-import isc.Util
 import isc.config

Modified: branches/jinmei-asio/src/lib/python/isc/auth/Makefile.am
==============================================================================
--- branches/jinmei-asio/src/lib/python/isc/auth/Makefile.am (original)
+++ branches/jinmei-asio/src/lib/python/isc/auth/Makefile.am Tue Mar  9 22:42:40 2010
@@ -1,6 +1,3 @@
-PY_MODULES=	__init__.py master.py sqlite3_ds.py
+python_PYTHON = __init__.py master.py sqlite3_ds.py
 
-install-data-local:
-	$(mkinstalldirs) $(DESTDIR)$(pyexecdir)/isc/auth
-	@(for _foo_ in $(PY_MODULES) ; \
-		do $(INSTALL) -m 0644 $(top_srcdir)/src/lib/python/isc/auth/$$_foo_ $(DESTDIR)$(pyexecdir)/isc/auth/; done)
+pythondir = $(pyexecdir)/isc/auth

Modified: branches/jinmei-asio/src/lib/python/isc/auth/master.py
==============================================================================
--- branches/jinmei-asio/src/lib/python/isc/auth/master.py (original)
+++ branches/jinmei-asio/src/lib/python/isc/auth/master.py Tue Mar  9 22:42:40 2010
@@ -90,8 +90,11 @@
 #########################################################################
 def pop(line):
     list = line.split()
-    first = list[0]
-    rest = ' '.join(list[1:])
+    first, rest = '', ''
+    if len(list) != 0:
+        first = list[0]
+    if len(list) > 1:
+        rest = ' '.join(list[1:])
     return first, rest
 
 #########################################################################
@@ -133,7 +136,7 @@
 name_regex = re.compile('[-\w\$\d\/*]+(?:\.[-\w\$\d\/]+)*\.?')
 def isname(s):
     global name_regex
-    if name_regex.match(s):
+    if s == '.' or name_regex.match(s):
         return True
     else:
         return False
@@ -190,18 +193,20 @@
     first, more = pop(s)
     second, more = pop(more)
     if re.match('\$origin', first, re.I):
-        if not isname(second):
+        if not second or not isname(second):
             raise MasterFileError('Invalid $ORIGIN')
         if more:
             raise MasterFileError('Invalid $ORIGIN')
-        if second[-1] == '.':
+        if second == '.':
+            origin = ''
+        elif second[-1] == '.':
             origin = second
         else:
             origin = second + '.' + origin
         return True
     elif re.match('\$ttl', first, re.I):
-        if not isttl(second):
-            raise MasterFileError('Invalid $TTL: ' + second)
+        if not second or not isttl(second):
+            raise MasterFileError('Invalid TTL: "' + second + '"')
         if more:
             raise MasterFileError('Invalid $TTL statement')
         defttl = parse_ttl(second)
@@ -335,17 +340,20 @@
 #########################################################################
 def reset():
     global defttl, origin
-    defttl = -1
-    origin=''
+    defttl = ''
+    origin = ''
 
 #########################################################################
 # openzone: open a zone master file, set initial origin, return descriptor
 #########################################################################
-def openzone(filename, initial_origin = '.'):
+def openzone(filename, initial_origin = ''):
+    global origin
     try:
         zf = open(filename, 'r')
     except:
-        return
+        raise MasterFileError("Could not open " + filename)
+    if initial_origin == '.':
+        initial_origin = ''
     origin = initial_origin
     return zf
 
@@ -370,10 +378,13 @@
             sub.close()
             continue
 
-        first = record.split()[0]
-        if first == '@':
-            name = origin
-            at, record = pop(record)
+        # replace @ with origin
+        rl = record.split()
+        if rl[0] == '@':
+            rl[0] = origin
+            if not origin:
+                rl[0] = '.'
+            record = ' '.join(rl)
 
         result = four(record, name)
 
@@ -398,15 +409,31 @@
         if rrclass.upper() != 'IN':
             raise MasterFileError("CH and HS zones not supported")
 
-        # add origin to rdata if necessary
-        if rrtype.lower() in ('cname', 'dname', 'ns'):
+        if not ttl:
+            raise MasterFileError("No TTL specified; zone rejected")
+
+        # add origin to rdata containing names, if necessary
+        if rrtype.lower() in ('cname', 'dname', 'ns', 'ptr'):
             if not isname(rdata):
                 raise MasterFileError("Invalid " + rrtype + ": " + rdata)
             if rdata[-1] != '.':
                 rdata += '.' + origin
-
-        if (ttl == -1):
-            raise MasterFileError("No TTL specified; zone rejected")
+        if rrtype.lower() == 'soa':
+            soa = rdata.split()
+            if len(soa) < 2 or not isname(soa[0]) or not isname(soa[1]):
+                raise MasterFileError("Invalid " + rrtype + ": " + rdata)
+            if soa[0][-1] != '.':
+                soa[0] += '.' + origin
+            if soa[1][-1] != '.':
+                soa[1] += '.' + origin
+            rdata = ' '.join(soa)
+        if rrtype.lower() == 'mx':
+            mx = rdata.split()
+            if len(mx) != 2 or not isname(mx[1]):
+                raise MasterFileError("Invalid " + rrtype + ": " + rdata)
+            if mx[1][-1] != '.':
+                mx[1] += '.' + origin
+                rdata = ' '.join(mx)
 
         yield (name, ttl, rrclass, rrtype, rdata)
 
@@ -414,9 +441,11 @@
 # zonename: scans zone data for an SOA record, returns its name, restores
 # the zone file to its prior state
 #########################################################################
-def zonename(zone, initial_origin = '.'):
+def zonename(zone, initial_origin = ''):
     global origin
     old_origin = origin
+    if initial_origin == '.':
+        initial_origin = ''
     origin = initial_origin
     old_location = zone.tell()
     zone.seek(0)

Modified: branches/jinmei-asio/src/lib/python/isc/auth/sqlite3_ds.py
==============================================================================
--- branches/jinmei-asio/src/lib/python/isc/auth/sqlite3_ds.py (original)
+++ branches/jinmei-asio/src/lib/python/isc/auth/sqlite3_ds.py Tue Mar  9 22:42:40 2010
@@ -129,29 +129,34 @@
     cur.execute("INSERT INTO zones (name, rdclass) VALUES (?, 'IN')", [temp])
     new_zone_id = cur.lastrowid
 
-    for name, ttl, rdclass, rdtype, rdata in reader(file):
-        sigtype = ''
-        if rdtype.lower() == 'rrsig':
-            sigtype = rdata.split()[0]
+    try:
+        for name, ttl, rdclass, rdtype, rdata in reader(file):
+            sigtype = ''
+            if rdtype.lower() == 'rrsig':
+                sigtype = rdata.split()[0]
 
-        if rdtype.lower() == 'nsec3' or sigtype.lower() == 'nsec3':
-            hash = name.split('.')[0]
-            cur.execute("""INSERT INTO nsec3
-                           (zone_id, hash, owner, ttl, rdtype, rdata)
-                           VALUES (?, ?, ?, ?, ?, ?)""",
-                        [new_zone_id, hash, name, ttl, rdtype, rdata])
-        elif rdtype.lower() == 'rrsig':
-            cur.execute("""INSERT INTO records
-                           (zone_id, name, rname, ttl, rdtype, sigtype, rdata)
-                           VALUES (?, ?, ?, ?, ?, ?, ?)""",
-                        [new_zone_id, name, reverse_name(name), ttl,
-                        rdtype, sigtype, rdata])
-        else:
-            cur.execute("""INSERT INTO records
-                           (zone_id, name, rname, ttl, rdtype, rdata)
-                           VALUES (?, ?, ?, ?, ?, ?)""",
-                        [new_zone_id, name, reverse_name(name), ttl,
-                        rdtype, rdata])
+            if rdtype.lower() == 'nsec3' or sigtype.lower() == 'nsec3':
+                hash = name.split('.')[0]
+                cur.execute("""INSERT INTO nsec3
+                               (zone_id, hash, owner, ttl, rdtype, rdata)
+                               VALUES (?, ?, ?, ?, ?, ?)""",
+                            [new_zone_id, hash, name, ttl, rdtype, rdata])
+            elif rdtype.lower() == 'rrsig':
+                cur.execute("""INSERT INTO records
+                               (zone_id, name, rname, ttl,
+                                rdtype, sigtype, rdata)
+                               VALUES (?, ?, ?, ?, ?, ?, ?)""",
+                            [new_zone_id, name, reverse_name(name), ttl,
+                            rdtype, sigtype, rdata])
+            else:
+                cur.execute("""INSERT INTO records
+                               (zone_id, name, rname, ttl, rdtype, rdata)
+                               VALUES (?, ?, ?, ?, ?, ?)""",
+                            [new_zone_id, name, reverse_name(name), ttl,
+                            rdtype, rdata])
+    except Exception as e:
+        fail = "Error while loading " + zone + ": " + e.args[0]
+        raise Sqlite3DSError(fail)
 
     if old_zone_id:
         cur.execute("DELETE FROM zones WHERE id=?", [old_zone_id])

Modified: branches/jinmei-asio/src/lib/python/isc/cc/Makefile.am
==============================================================================
--- branches/jinmei-asio/src/lib/python/isc/cc/Makefile.am (original)
+++ branches/jinmei-asio/src/lib/python/isc/cc/Makefile.am Tue Mar  9 22:42:40 2010
@@ -1,6 +1,3 @@
-PY_MODULES=	__init__.py data.py session.py message.py
+python_PYTHON =	__init__.py data.py session.py message.py
 
-install-data-local:
-	$(mkinstalldirs) $(DESTDIR)$(pyexecdir)/isc/cc
-	@(for _foo_ in $(PY_MODULES) ; \
-		do $(INSTALL) -m 0644 $(top_srcdir)/src/lib/python/isc/cc/$$_foo_ $(DESTDIR)$(pyexecdir)/isc/cc/ ; done)
+pythondir = $(pyexecdir)/isc/cc

Modified: branches/jinmei-asio/src/lib/python/isc/cc/message.py
==============================================================================
--- branches/jinmei-asio/src/lib/python/isc/cc/message.py (original)
+++ branches/jinmei-asio/src/lib/python/isc/cc/message.py Tue Mar  9 22:42:40 2010
@@ -12,9 +12,6 @@
 # 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.
-
-# XXX
-import isc.Util
 
 import sys
 import struct

Modified: branches/jinmei-asio/src/lib/python/isc/config/Makefile.am
==============================================================================
--- branches/jinmei-asio/src/lib/python/isc/config/Makefile.am (original)
+++ branches/jinmei-asio/src/lib/python/isc/config/Makefile.am Tue Mar  9 22:42:40 2010
@@ -1,6 +1,3 @@
-PY_MODULES=	__init__.py ccsession.py cfgmgr.py config_data.py module_spec.py
+python_PYTHON = __init__.py ccsession.py cfgmgr.py config_data.py module_spec.py
 
-install-data-local:
-	$(mkinstalldirs) $(DESTDIR)$(pyexecdir)/isc/config
-	@(for _foo_ in $(PY_MODULES) ; \
-		do $(INSTALL) -m 0644 $(top_srcdir)/src/lib/python/isc/config/$$_foo_ $(DESTDIR)$(pyexecdir)/isc/config/; done)
+pythondir = $(pyexecdir)/isc/config

Modified: branches/jinmei-asio/src/lib/python/isc/config/ccsession.py
==============================================================================
--- branches/jinmei-asio/src/lib/python/isc/config/ccsession.py (original)
+++ branches/jinmei-asio/src/lib/python/isc/config/ccsession.py Tue Mar  9 22:42:40 2010
@@ -195,8 +195,6 @@
                             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)
-                            print("[XX] updated remote config value: ")
-                            print(newc)
                             return
 
                     # ok, so apparently this update is for us.

Modified: branches/jinmei-asio/src/lib/python/isc/config/cfgmgr.py
==============================================================================
--- branches/jinmei-asio/src/lib/python/isc/config/cfgmgr.py (original)
+++ branches/jinmei-asio/src/lib/python/isc/config/cfgmgr.py Tue Mar  9 22:42:40 2010
@@ -267,8 +267,10 @@
             got_error = False
             err_list = []
             for module in self.config.data:
-                if module != "version":
+                if module != "version" and self.config.data[module] != old_data[module]:
                     update_cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, self.config.data[module])
+                    print("[XX] send update: " + str(update_cmd))
+                    print("[XX] to: " + str(module))
                     self.cc.group_sendmsg(update_cmd, module)
                     answer, env = self.cc.group_recvmsg(False)
                     if answer == None:




More information about the bind10-changes mailing list