BIND 10 trac704, updated. 50eff6d6d5788193d055c038a5772bce2534628f Merge branch 'master' into trac704

BIND 10 source code commits bind10-changes at lists.isc.org
Tue Apr 19 10:27:19 UTC 2011


The branch, trac704 has been updated
       via  50eff6d6d5788193d055c038a5772bce2534628f (commit)
       via  981abdeb847bfcee7c03d6c252d0859c1ed7a2b0 (commit)
       via  1781090510f9f7ad378497d65feaf94f5c32b262 (commit)
       via  5651434ffff461b28616ffec595ff8a941c6df04 (commit)
       via  27dc0ba25fb06503a6e9f5bfaa293b1db1cd5e78 (commit)
       via  ec9b5b21e964c7fb5eeeedb17af5a4f833289667 (commit)
       via  5a906f923ec419895fc398ba3dcdf64842164d14 (commit)
       via  95689208a5f10f7f839abc57d7db9d73e0eca200 (commit)
       via  9303e9a05b5a48f80ea32531ae493724b04aa2be (commit)
       via  8ac28e2101ab399e885c178294635cacfe6573e1 (commit)
       via  def088d4e8387e8d04fc0fc0e56a9db39e3ccefa (commit)
       via  4578fa114fea13eae53713b5282f5e906a71fbf6 (commit)
       via  e58e69f00b57fbb7bdddfbd43e8b958ea50fd146 (commit)
       via  b91c7c259c85f29413e05898e8b74ca011835aa6 (commit)
       via  111334856c3beb8cfad23ff60264604746dd1492 (commit)
       via  2e11f6cdbae2c29e12bbce2c9d14f1851439155f (commit)
      from  ea064b169fe62c3f71aaec4b5c6d677255f3621a (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 50eff6d6d5788193d055c038a5772bce2534628f
Merge: ea064b169fe62c3f71aaec4b5c6d677255f3621a 981abdeb847bfcee7c03d6c252d0859c1ed7a2b0
Author: Stephen Morris <stephen at isc.org>
Date:   Tue Apr 19 11:09:21 2011 +0100

    Merge branch 'master' into trac704

commit 981abdeb847bfcee7c03d6c252d0859c1ed7a2b0
Merge: 1781090510f9f7ad378497d65feaf94f5c32b262 5651434ffff461b28616ffec595ff8a941c6df04
Author: Jelte Jansen <jelte at isc.org>
Date:   Tue Apr 19 10:51:56 2011 +0200

    Merge branch 'master' of ssh://bind10.isc.org/var/bind10/git/bind10

commit 1781090510f9f7ad378497d65feaf94f5c32b262
Author: Jelte Jansen <jelte at isc.org>
Date:   Tue Apr 19 10:51:12 2011 +0200

    [master] Add (guarded) inclusion of unistd.h for sunstudio build

commit 5651434ffff461b28616ffec595ff8a941c6df04
Merge: 5a906f923ec419895fc398ba3dcdf64842164d14 27dc0ba25fb06503a6e9f5bfaa293b1db1cd5e78
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Tue Apr 19 09:35:09 2011 +0200

    Merge branch 'work/loneconfig'

commit 27dc0ba25fb06503a6e9f5bfaa293b1db1cd5e78
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Tue Apr 19 09:30:55 2011 +0200

    [trac810] Put README in EXTRA_DIST

commit ec9b5b21e964c7fb5eeeedb17af5a4f833289667
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Tue Apr 19 09:30:32 2011 +0200

    [trac810] TODO note

commit 5a906f923ec419895fc398ba3dcdf64842164d14
Author: Stephen Morris <stephen at isc.org>
Date:   Mon Apr 18 06:47:56 2011 -0700

    [master] Changes to fix build under OS X/clang++
    
    Changes were reviewed and approved in the Jabber room.

commit 95689208a5f10f7f839abc57d7db9d73e0eca200
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Mon Apr 18 10:35:43 2011 +0200

    [trac810] README about plugins

commit 9303e9a05b5a48f80ea32531ae493724b04aa2be
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Mon Apr 18 10:14:18 2011 +0200

    [trac810] Find the plugins from build

commit 8ac28e2101ab399e885c178294635cacfe6573e1
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Mon Apr 18 10:10:50 2011 +0200

    [trac810] Fix Makefile

commit def088d4e8387e8d04fc0fc0e56a9db39e3ccefa
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Wed Apr 13 19:09:12 2011 +0200

    [trac810] Provide a notification on virtual config
    
    The notification is used even when the config is not checked by remote
    config. So we send it out in case of virtual module as well, but don't
    wait for the answer.

commit 4578fa114fea13eae53713b5282f5e906a71fbf6
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Sat Apr 9 12:41:03 2011 +0200

    [trac810] Call the plugin loader

commit e58e69f00b57fbb7bdddfbd43e8b958ea50fd146
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Sat Apr 9 12:02:19 2011 +0200

    [trac810] Plugin loader

commit b91c7c259c85f29413e05898e8b74ca011835aa6
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Fri Apr 8 18:19:40 2011 +0200

    [trac810] Test for plugin-loader

commit 111334856c3beb8cfad23ff60264604746dd1492
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Fri Apr 8 15:35:00 2011 +0200

    [trac810] Call checkers with virtual modules

commit 2e11f6cdbae2c29e12bbce2c9d14f1851439155f
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Fri Apr 8 13:41:39 2011 +0200

    [trac810] Functions to add virtual modules

-----------------------------------------------------------------------

Summary of changes:
 configure.ac                                       |    1 +
 src/bin/cfgmgr/Makefile.am                         |    2 +-
 src/bin/cfgmgr/b10-cfgmgr.py.in                    |   28 ++++++++
 src/bin/cfgmgr/plugins/Makefile.am                 |    1 +
 src/bin/cfgmgr/plugins/README                      |   34 +++++++++
 src/bin/cfgmgr/tests/Makefile.am                   |    3 +-
 src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in         |   31 +++++++++
 .../cfgmgr/tests/testdata/plugins/testplugin.py}   |   30 +++++----
 src/lib/python/isc/config/cfgmgr.py                |   59 +++++++++++++---
 src/lib/python/isc/config/tests/cfgmgr_test.py     |   71 ++++++++++++++++++++
 tests/tools/badpacket/Makefile.am                  |    5 ++
 tests/tools/badpacket/scan.cc                      |    6 ++
 12 files changed, 245 insertions(+), 26 deletions(-)
 create mode 100644 src/bin/cfgmgr/plugins/Makefile.am
 create mode 100644 src/bin/cfgmgr/plugins/README
 copy src/{lib/python/isc/testutils/parse_args.py => bin/cfgmgr/tests/testdata/plugins/testplugin.py} (52%)

-----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 63bd985..9018f2e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -612,6 +612,7 @@ AC_CONFIG_FILES([Makefile
                  src/bin/bindctl/Makefile
                  src/bin/bindctl/tests/Makefile
                  src/bin/cfgmgr/Makefile
+                 src/bin/cfgmgr/plugins/Makefile
                  src/bin/cfgmgr/tests/Makefile
                  src/bin/host/Makefile
                  src/bin/loadzone/Makefile
diff --git a/src/bin/cfgmgr/Makefile.am b/src/bin/cfgmgr/Makefile.am
index a41448b..fc0ed4a 100644
--- a/src/bin/cfgmgr/Makefile.am
+++ b/src/bin/cfgmgr/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = . tests
+SUBDIRS = . plugins tests
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 
diff --git a/src/bin/cfgmgr/b10-cfgmgr.py.in b/src/bin/cfgmgr/b10-cfgmgr.py.in
index 5355582..607a6dc 100755
--- a/src/bin/cfgmgr/b10-cfgmgr.py.in
+++ b/src/bin/cfgmgr/b10-cfgmgr.py.in
@@ -23,6 +23,8 @@ import isc.util.process
 import signal
 import os
 from optparse import OptionParser
+import glob
+import os.path
 
 isc.util.process.rename()
 
@@ -39,9 +41,11 @@ if "B10_FROM_SOURCE" in os.environ:
         DATA_PATH = os.environ["B10_FROM_SOURCE_LOCALSTATEDIR"]
     else:
         DATA_PATH = os.environ["B10_FROM_SOURCE"]
+    PLUGIN_PATH = [DATA_PATH + '/src/bin/cfgmgr/plugins']
 else:
     PREFIX = "@prefix@"
     DATA_PATH = "@localstatedir@/@PACKAGE@".replace("${prefix}", PREFIX)
+    PLUGIN_PATHS = ["@prefix@/share/@PACKAGE@/config_plugins"]
 DEFAULT_CONFIG_FILE = "b10-config.db"
 
 cm = None
@@ -65,6 +69,28 @@ def signal_handler(signal, frame):
     if cm:
         cm.running = False
 
+def load_plugins(path, cm):
+    """Load all python files in the given path and treat them as plugins."""
+    # Find the python files
+    plugins = glob.glob(path + os.sep + '*.py')
+    # Search this directory first, but leave the others there for the imports
+    # of the modules
+    sys.path.insert(0, path)
+    try:
+        for plugin in plugins:
+            # Generate the name of the plugin
+            filename = os.path.basename(plugin)
+            name = filename[:-3]
+            # Load it
+            module = __import__(name)
+            # Ask it to provide the spec and checking function
+            (spec, check_func) = module.load()
+            # And insert it into the manager
+            cm.set_virtual_module(spec, check_func)
+    finally:
+        # Restore the search path
+        sys.path = sys.path[1:]
+
 def main():
     options = parse_options()
     global cm
@@ -73,6 +99,8 @@ def main():
         signal.signal(signal.SIGINT, signal_handler)
         signal.signal(signal.SIGTERM, signal_handler)
         cm.read_config()
+        for ppath in PLUGIN_PATHS:
+            load_plugins(ppath, cm)
         cm.notify_boss()
         cm.run()
     except SessionError as se:
diff --git a/src/bin/cfgmgr/plugins/Makefile.am b/src/bin/cfgmgr/plugins/Makefile.am
new file mode 100644
index 0000000..952fde6
--- /dev/null
+++ b/src/bin/cfgmgr/plugins/Makefile.am
@@ -0,0 +1 @@
+EXTRA_DIST = README
diff --git a/src/bin/cfgmgr/plugins/README b/src/bin/cfgmgr/plugins/README
new file mode 100644
index 0000000..27fb0c0
--- /dev/null
+++ b/src/bin/cfgmgr/plugins/README
@@ -0,0 +1,34 @@
+How to write a configuration plugin
+===================================
+
+The plugins are used to describe configuration modules that have no hosting
+process. Therefore there's no process to provide their specification and to
+check them for correctness. So the plugin takes this responsibility.
+
+Each plugin is a python file installed into the
+`@prefix@/share/@PACKAGE@/config_plugins` directory (usually
+`/usr/share/bind10/config_plugins`). It is loaded automatically at startup.
+
+The entrypoint of a plugin is function called `load()`. It should return a
+tuple, first value should be the module specification (usually instance of
+`isc.config.module_spec.ModuleSpec`, loaded by `module_spec_from_file()`).
+
+The second value is a callable object. It will be called whenever the
+configuration of module needs to be checked. The only parameter will be the new
+config (as python dictionary). To accept the new configuration, return None. If
+you return a string, it is taken as an error message. If you raise an
+exception, the config is rejected as well, however it is not considered a
+graceful rejection, but a failure of the module.
+
+So, this is how a plugin could look like:
+
+  from isc.config.module_spec import module_spec_from_file
+
+  def check(config):
+      if config['bogosity'] > 1:
+          return "Too bogus to live with"
+      else:
+          return None
+
+  def load():
+      return (module_spec_from_file('module_spec.spec'), check)
diff --git a/src/bin/cfgmgr/tests/Makefile.am b/src/bin/cfgmgr/tests/Makefile.am
index 16e6223..68666e6 100644
--- a/src/bin/cfgmgr/tests/Makefile.am
+++ b/src/bin/cfgmgr/tests/Makefile.am
@@ -1,7 +1,7 @@
 PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
 PYTESTS = b10-cfgmgr_test.py
 
-EXTRA_DIST = $(PYTESTS)
+EXTRA_DIST = $(PYTESTS) testdata/plugins/testplugin.py
 
 # test using command-line arguments, so use check-local target instead of TESTS
 check-local:
@@ -12,6 +12,7 @@ if ENABLE_PYTHON_COVERAGE
 endif
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
+	env TESTDATA_PATH=$(abs_srcdir)/testdata \
 	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/cfgmgr \
 	$(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \
 	done
diff --git a/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in b/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in
index 037a106..37cd0f5 100644
--- a/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in
+++ b/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in
@@ -30,6 +30,7 @@ class MyConfigManager:
         self.run_called = False
         self.write_config_called = False
         self.running = True
+        self.virtual_modules = []
 
     def read_config(self):
         self.read_config_called = True
@@ -43,6 +44,24 @@ class MyConfigManager:
     def write_config(self):
         self.write_config_called = True
 
+    def set_virtual_module(self, spec, function):
+        self.virtual_modules.append((spec, function))
+
+class TestPlugins(unittest.TestCase):
+    def test_load_plugins(self):
+        """Test we can successfully find and load the mock plugin."""
+        # Let it load the plugin
+        b = __import__("b10-cfgmgr")
+        # The parameters aren't important for this test
+        cm = MyConfigManager(None, None)
+        b.load_plugins(os.environ['TESTDATA_PATH'] + os.sep + 'plugins', cm)
+        # Check exactly one plugin was loaded and the right data were fed into
+        # the cm
+        self.assertEqual(len(cm.virtual_modules), 1)
+        (spec, check) = cm.virtual_modules[0]
+        self.assertEqual(spec.get_module_name(), "mock_config_plugin")
+        self.assertEqual(check(None), "Mock config plugin")
+
 class TestConfigManagerStartup(unittest.TestCase):
     def test_cfgmgr(self):
         # some creative module use;
@@ -50,13 +69,24 @@ class TestConfigManagerStartup(unittest.TestCase):
         # this also gives us the chance to override the imported
         # module ConfigManager in it.
         b = __import__("b10-cfgmgr")
+        orig_load = b.load_plugins
+        b.PLUGIN_PATHS = ["/plugin/path"]
+        self.loaded_plugins = False
+        def load_plugins(path, cm):
+            # Check it's called with proper arguments
+            self.assertEqual(path, "/plugin/path")
+            self.assertTrue(isinstance(cm, MyConfigManager))
+            self.loaded_plugins = True
+        b.load_plugins = load_plugins
         b.ConfigManager = MyConfigManager
 
         b.main()
+        b.load_plugins = orig_load
 
         self.assertTrue(b.cm.read_config_called)
         self.assertTrue(b.cm.notify_boss_called)
         self.assertTrue(b.cm.run_called)
+        self.assertTrue(self.loaded_plugins)
         # if there are no changes, config is not written
         self.assertFalse(b.cm.write_config_called)
 
@@ -81,6 +111,7 @@ class TestConfigManagerStartup(unittest.TestCase):
 
         os.environ["B10_FROM_SOURCE"] = tmp_env_var
         b = __import__("b10-cfgmgr", globals(), locals())
+        b.PLUGIN_PATH = [] # It's enough to test plugins in one test
         b.ConfigManager = MyConfigManager
         self.assertEqual(tmp_env_var, b.DATA_PATH)
 
diff --git a/src/bin/cfgmgr/tests/testdata/plugins/testplugin.py b/src/bin/cfgmgr/tests/testdata/plugins/testplugin.py
new file mode 100644
index 0000000..a50eefe
--- /dev/null
+++ b/src/bin/cfgmgr/tests/testdata/plugins/testplugin.py
@@ -0,0 +1,34 @@
+# Copyright (C) 2011  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# A test plugin. It does mostly nothing, just provides a function we can
+# recognize from the test. However, it looks like a real plugin, with the
+# (almost) correct interface, even when it's not used.
+
+class MockSpec:
+    """Mock spec, contains no data, it can only provide it's name.
+       It'll not really be used for anything."""
+    def get_module_name(self):
+        return "mock_config_plugin"
+
+def mock_check_config(config):
+    """Mock function to check config. Does nothing, only returns
+       an "error" string to indicate it's this one."""
+    return "Mock config plugin"
+
+def load():
+    """When a plugin is loaded, this is called to provide the spec and
+       checking function."""
+    return (MockSpec(), mock_check_config)
diff --git a/src/lib/python/isc/config/cfgmgr.py b/src/lib/python/isc/config/cfgmgr.py
index 8561378..88a93e1 100644
--- a/src/lib/python/isc/config/cfgmgr.py
+++ b/src/lib/python/isc/config/cfgmgr.py
@@ -170,6 +170,10 @@ class ConfigManager:
         self.data_path = data_path
         self.database_filename = database_filename
         self.module_specs = {}
+        # Virtual modules are the ones which have no process running. The
+        # checking of validity is done by functions presented here instead
+        # of some other process
+        self.virtual_modules = {}
         self.config = ConfigManagerData(data_path, database_filename)
         if session:
             self.cc = session
@@ -187,11 +191,20 @@ class ConfigManager:
         """Adds a ModuleSpec"""
         self.module_specs[spec.get_module_name()] = spec
 
+    def set_virtual_module(self, spec, check_func):
+        """Adds a virtual module with its spec and checking function."""
+        self.module_specs[spec.get_module_name()] = spec
+        self.virtual_modules[spec.get_module_name()] = check_func
+
     def remove_module_spec(self, module_name):
         """Removes the full ModuleSpec for the given module_name.
+           Also removes the virtual module check function if it
+           was present.
            Does nothing if the module was not present."""
         if module_name in self.module_specs:
             del self.module_specs[module_name]
+        if module_name in self.virtual_modules:
+            del self.virtual_modules[module_name]
 
     def get_module_spec(self, module_name = None):
         """Returns the full ModuleSpec for the module with the given
@@ -299,24 +312,48 @@ class ConfigManager:
         # todo: use api (and check the data against the definition?)
         old_data = copy.deepcopy(self.config.data)
         conf_part = data.find_no_exc(self.config.data, module_name)
+        update_cmd = None
+        use_part = None
         if conf_part:
             data.merge(conf_part, cmd)
-            update_cmd = ccsession.create_command(ccsession.COMMAND_CONFIG_UPDATE,
-                                                  conf_part)
-            seq = self.cc.group_sendmsg(update_cmd, module_name)
-            try:
-                answer, env = self.cc.group_recvmsg(False, seq)
-            except isc.cc.SessionTimeout:
-                answer = ccsession.create_answer(1, "Timeout waiting for answer from " + module_name)
+            use_part = conf_part
         else:
             conf_part = data.set(self.config.data, module_name, {})
             data.merge(conf_part[module_name], cmd)
-            # send out changed info
-            update_cmd = ccsession.create_command(ccsession.COMMAND_CONFIG_UPDATE,
-                                                  conf_part[module_name])
+            use_part = conf_part[module_name]
+
+        # The command to send
+        update_cmd = ccsession.create_command(ccsession.COMMAND_CONFIG_UPDATE,
+                                              use_part)
+
+        # TODO: This design might need some revisiting. We might want some
+        # polymorphism instead of branching. But it just might turn out it
+        # will get solved by itself when we move everything to virtual modules
+        # (which is possible solution to the offline configuration problem)
+        # or when we solve the incorect behaviour here when a config is
+        # rejected (spying modules don't know it was rejected and some modules
+        # might have been commited already).
+        if module_name in self.virtual_modules:
+            # The module is virtual, so call it to get the answer
+            try:
+                error = self.virtual_modules[module_name](use_part)
+                if error is None:
+                    answer = ccsession.create_answer(0)
+                    # OK, it is successful, send the notify, but don't wait
+                    # for answer
+                    seq = self.cc.group_sendmsg(update_cmd, module_name)
+                else:
+                    answer = ccsession.create_answer(1, error)
+            # Make sure just a validating plugin don't kill the whole manager
+            except Exception as excp:
+                # Provide answer
+                answer = ccsession.create_answer(1, "Exception: " + str(excp))
+        else:
+            # Real module, send it over the wire to it
+            # send out changed info and wait for answer
             seq = self.cc.group_sendmsg(update_cmd, module_name)
-            # replace 'our' answer with that of the module
             try:
+                # replace 'our' answer with that of the module
                 answer, env = self.cc.group_recvmsg(False, seq)
             except isc.cc.SessionTimeout:
                 answer = ccsession.create_answer(1, "Timeout waiting for answer from " + module_name)
diff --git a/src/lib/python/isc/config/tests/cfgmgr_test.py b/src/lib/python/isc/config/tests/cfgmgr_test.py
index 9534e14..b06db31 100644
--- a/src/lib/python/isc/config/tests/cfgmgr_test.py
+++ b/src/lib/python/isc/config/tests/cfgmgr_test.py
@@ -135,6 +135,8 @@ class TestConfigManager(unittest.TestCase):
         self.assert_(module_spec.get_module_name() not in self.cm.module_specs)
         self.cm.set_module_spec(module_spec)
         self.assert_(module_spec.get_module_name() in self.cm.module_specs)
+        self.assert_(module_spec.get_module_name() not in
+                     self.cm.virtual_modules)
 
     def test_remove_module_spec(self):
         module_spec = isc.config.module_spec.module_spec_from_file(self.data_path + os.sep + "spec1.spec")
@@ -143,6 +145,30 @@ class TestConfigManager(unittest.TestCase):
         self.assert_(module_spec.get_module_name() in self.cm.module_specs)
         self.cm.remove_module_spec(module_spec.get_module_name())
         self.assert_(module_spec.get_module_name() not in self.cm.module_specs)
+        self.assert_(module_spec.get_module_name() not in
+                     self.cm.virtual_modules)
+
+    def test_add_remove_virtual_module(self):
+        module_spec = isc.config.module_spec.module_spec_from_file(
+            self.data_path + os.sep + "spec1.spec")
+        check_func = lambda: True
+        # Make sure it's not in there before
+        self.assert_(module_spec.get_module_name() not in self.cm.module_specs)
+        self.assert_(module_spec.get_module_name() not in
+                     self.cm.virtual_modules)
+        # Add it there
+        self.cm.set_virtual_module(module_spec, check_func)
+        # Check it's in there
+        self.assert_(module_spec.get_module_name() in self.cm.module_specs)
+        self.assertEqual(self.cm.module_specs[module_spec.get_module_name()],
+                      module_spec)
+        self.assertEqual(self.cm.virtual_modules[module_spec.get_module_name()],
+                      check_func)
+        # Remove it again
+        self.cm.remove_module_spec(module_spec.get_module_name())
+        self.assert_(module_spec.get_module_name() not in self.cm.module_specs)
+        self.assert_(module_spec.get_module_name() not in
+                     self.cm.virtual_modules)
 
     def test_get_module_spec(self):
         module_spec = isc.config.module_spec.module_spec_from_file(self.data_path + os.sep + "spec1.spec")
@@ -312,6 +338,51 @@ class TestConfigManager(unittest.TestCase):
                                 },
                                 {'result': [0]})
 
+    def test_set_config_virtual(self):
+        """Test that if the module is virtual, we don't send it over the
+           message bus, but call the checking function.
+           """
+        # We run the same three times, with different return values
+        def single_test(value, returnFunc, expectedResult):
+            # Because closures can't assign to closed-in variables, we pass
+            # it trough self
+            self.called_with = None
+            def check_test(new_data):
+                self.called_with = new_data
+                return returnFunc()
+
+            # Register our virtual module
+            self.cm.set_virtual_module(self.spec, check_test)
+            # The fake session will throw now if it tries to read a response.
+            # Handy, we don't need to find a complicated way to check for it.
+            result = self.cm._handle_set_config_module(self.spec.
+                                                       get_module_name(),
+                                                       {'item1': value})
+            # Check the correct result is passed and our function was called
+            # With correct data
+            self.assertEqual(self.called_with['item1'], value)
+            self.assertEqual(result, {'result': expectedResult})
+            if expectedResult[0] == 0:
+                # Check it provided the correct notification
+                self.assertEqual(len(self.fake_session.message_queue), 1)
+                self.assertEqual({'command': [ 'config_update',
+                                 {'item1': value}]},
+                                 self.fake_session.get_message('Spec2', None))
+                # and the queue should now be empty again
+                self.assertEqual(len(self.fake_session.message_queue), 0)
+            else:
+                # It shouldn't send anything on error
+                self.assertEqual(len(self.fake_session.message_queue), 0)
+
+        # Success
+        single_test(5, lambda: None, [0])
+        # Graceful error
+        single_test(6, lambda: "Just error", [1, "Just error"])
+        # Exception from the checker
+        def raiser():
+            raise Exception("Just exception")
+        single_test(7, raiser, [1, "Exception: Just exception"])
+
     def test_set_config_all(self):
         my_ok_answer = { 'result': [ 0 ] }
 
diff --git a/tests/tools/badpacket/Makefile.am b/tests/tools/badpacket/Makefile.am
index b249f0e..7df7077 100644
--- a/tests/tools/badpacket/Makefile.am
+++ b/tests/tools/badpacket/Makefile.am
@@ -20,6 +20,11 @@ badpacket_SOURCES += option_info.cc option_info.h
 badpacket_SOURCES += scan.cc scan.h
 badpacket_SOURCES += version.h
 
+badpacket_CXXFLAGS = $(AM_CXXFLAGS)
+if USE_CLANGPP
+badpacket_CXXFLAGS += -Wno-error
+endif
+
 badpacket_LDADD  = $(top_builddir)/src/lib/asiodns/libasiodns.la
 badpacket_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
 badpacket_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
diff --git a/tests/tools/badpacket/scan.cc b/tests/tools/badpacket/scan.cc
index 7b843b4..07263ce 100644
--- a/tests/tools/badpacket/scan.cc
+++ b/tests/tools/badpacket/scan.cc
@@ -19,6 +19,12 @@
 
 #include <stdlib.h>
 
+#include <config.h>
+
+// on sunstudio, asio.hpp needs unistd.h for pipe() to be defined
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
 #include <asio.hpp>
 
 #include <asiolink/io_address.h>




More information about the bind10-changes mailing list