BIND 10 trac640, updated. 287e17100b60fbfceacc7337cd6ac9bab36185be [640] add lettuce test for removing modules
BIND 10 source code commits
bind10-changes at lists.isc.org
Fri Feb 3 17:34:52 UTC 2012
The branch, trac640 has been updated
via 287e17100b60fbfceacc7337cd6ac9bab36185be (commit)
via deadd3b30b20cad6964bebf38dba417c42b78289 (commit)
from 4a8fa93781cce0dbd33f55e604d87db511fb5903 (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 287e17100b60fbfceacc7337cd6ac9bab36185be
Author: Jelte Jansen <jelte at isc.org>
Date: Fri Feb 3 18:34:34 2012 +0100
[640] add lettuce test for removing modules
commit deadd3b30b20cad6964bebf38dba417c42b78289
Author: Jelte Jansen <jelte at isc.org>
Date: Fri Feb 3 17:22:16 2012 +0100
[640] address number of review comments
-----------------------------------------------------------------------
Summary of changes:
src/lib/config/ccsession.h | 12 +-
src/lib/config/config_messages.mes | 10 +-
src/lib/config/tests/ccsession_unittests.cc | 58 +++++++++--
src/lib/config/tests/fake_session.cc | 9 +-
src/lib/config/tests/fake_session.h | 9 ++
src/lib/python/isc/config/ccsession.py | 7 +-
src/lib/python/isc/config/cfgmgr.py | 4 +-
src/lib/python/isc/config/tests/cfgmgr_test.py | 7 +-
tests/lettuce/features/bindctl_commands.feature | 41 ++++++++
tests/lettuce/features/terrain/bind10_control.py | 121 +++++++++++++++++----
10 files changed, 225 insertions(+), 53 deletions(-)
create mode 100644 tests/lettuce/features/bindctl_commands.feature
-----------------------------------------------------------------------
diff --git a/src/lib/config/ccsession.h b/src/lib/config/ccsession.h
index a9806b9..d3225d2 100644
--- a/src/lib/config/ccsession.h
+++ b/src/lib/config/ccsession.h
@@ -192,12 +192,12 @@ public:
bool handle_logging = true
);
- /**
- * Destructor
- *
- * The desctructor automatically calls sendStopping(), which sends
- * a message to the ConfigManager that this module is stopping
- */
+ ///
+ /// Destructor
+ ///
+ /// The desctructor automatically calls sendStopping(), which sends
+ /// a message to the ConfigManager that this module is stopping
+ ///
~ModuleCCSession();
/// Start receiving new commands and configuration changes asynchronously.
diff --git a/src/lib/config/config_messages.mes b/src/lib/config/config_messages.mes
index e7db0ea..552256c 100644
--- a/src/lib/config/config_messages.mes
+++ b/src/lib/config/config_messages.mes
@@ -49,6 +49,11 @@ manager is appended to the log error. The most likely cause is that
the module is of a different (command specification) version than the
running configuration manager.
+% CONFIG_JSON_PARSE JSON parse error in %1: %2
+There was an error parsing the JSON file. The given file does not appear
+to be in valid JSON format. Please verify that the filename is correct
+and that the contents are valid JSON.
+
% CONFIG_LOG_EXPLICIT will use logging configuration for explicitly-named logger %1
This is a debug message. When processing the "loggers" part of the
configuration file, the configuration library found an entry for the named
@@ -74,11 +79,6 @@ wildcard entry (one containing the "*" character) that matches a logger
specification in the program. The logging configuration for the program
will be updated with the information.
-% CONFIG_JSON_PARSE JSON parse error in %1: %2
-There was an error parsing the JSON file. The given file does not appear
-to be in valid JSON format. Please verify that the filename is correct
-and that the contents are valid JSON.
-
% CONFIG_MOD_SPEC_FORMAT module specification error in %1: %2
The given file does not appear to be a valid specification file: details
are included in the message. Please verify that the filename is correct
diff --git a/src/lib/config/tests/ccsession_unittests.cc b/src/lib/config/tests/ccsession_unittests.cc
index f5480bd..abaff8e 100644
--- a/src/lib/config/tests/ccsession_unittests.cc
+++ b/src/lib/config/tests/ccsession_unittests.cc
@@ -26,6 +26,8 @@
#include <log/logger_name.h>
+#include <boost/scoped_ptr.hpp>
+
using namespace isc::data;
using namespace isc::config;
using namespace isc::cc;
@@ -197,17 +199,20 @@ TEST_F(CCSessionTest, session_close) {
std::string group, to;
EXPECT_FALSE(session.haveSubscription("Spec2", "*"));
- {
- ModuleCCSession mccs(ccspecfile("spec2.spec"), session, NULL, NULL,
- true, false);
- EXPECT_TRUE(session.haveSubscription("Spec2", "*"));
- // The initial message is irrelevant for this test
- // (see session2 test), drop it
- session.getFirstMessage(group, to);
- // Queue should now be empty
- ASSERT_EQ(0, session.getMsgQueue()->size());
- }
- // Destructor should have cause a new message
+
+ boost::scoped_ptr<ModuleCCSession> mccs(new ModuleCCSession(
+ ccspecfile("spec2.spec"),
+ session, NULL, NULL,
+ true, false));
+ EXPECT_TRUE(session.haveSubscription("Spec2", "*"));
+ // The initial message is irrelevant for this test
+ // (see session2 test), drop it
+ session.getFirstMessage(group, to);
+ // Queue should now be empty
+ ASSERT_EQ(0, session.getMsgQueue()->size());
+ // Invoke the destructor
+ mccs.reset();
+ // Destructor should have caused a new message
ASSERT_EQ(1, session.getMsgQueue()->size());
msg = session.getFirstMessage(group, to);
EXPECT_EQ("{ \"command\": [ \"stopping\", "
@@ -217,6 +222,37 @@ TEST_F(CCSessionTest, session_close) {
EXPECT_EQ(0, session.getMsgQueue()->size());
}
+TEST_F(CCSessionTest, session_close_exception) {
+ // Test whether an exception encountered during the destructor is
+ // handled correctly
+ ConstElementPtr msg;
+ std::string group, to;
+
+ EXPECT_FALSE(session.haveSubscription("Spec2", "*"));
+
+ boost::scoped_ptr<ModuleCCSession> mccs(new ModuleCCSession(
+ ccspecfile("spec2.spec"),
+ session, NULL, NULL,
+ true, false));
+ EXPECT_TRUE(session.haveSubscription("Spec2", "*"));
+ // The initial message is irrelevant for this test
+ // (see session2 test), drop it
+ session.getFirstMessage(group, to);
+ // Queue should now be empty
+ ASSERT_EQ(0, session.getMsgQueue()->size());
+
+ // Set fake session to throw an exception
+ session.setThrowOnSend(true);
+
+ // Invoke the destructor
+ mccs.reset();
+ // Destructor should not have caused a new message (since fakesession
+ // should have thrown an exception)
+ ASSERT_EQ(0, session.getMsgQueue()->size());
+ //EXPECT_EQ(0, session.getMsgQueue()->size());
+}
+
+
ConstElementPtr my_config_handler(ConstElementPtr new_config) {
if (new_config && new_config->contains("item1") &&
new_config->get("item1")->intValue() == 5) {
diff --git a/src/lib/config/tests/fake_session.cc b/src/lib/config/tests/fake_session.cc
index 2b216e7..177e629 100644
--- a/src/lib/config/tests/fake_session.cc
+++ b/src/lib/config/tests/fake_session.cc
@@ -72,7 +72,8 @@ FakeSession::FakeSession(isc::data::ElementPtr initial_messages,
messages_(initial_messages),
subscriptions_(subscriptions),
msg_queue_(msg_queue),
- started_(false)
+ started_(false),
+ throw_on_send_(false)
{
}
@@ -181,8 +182,9 @@ int
FakeSession::group_sendmsg(ConstElementPtr msg, std::string group,
std::string to, std::string)
{
- //cout << "[XX] client sends message: " << msg << endl;
- //cout << "[XX] to: " << group << " . " << instance << "." << to << endl;
+ if (throw_on_send_) {
+ isc_throw(Exception, "Throw on send is set in FakeSession");
+ }
addMessage(msg, group, to);
return (1);
}
@@ -261,6 +263,5 @@ FakeSession::haveSubscription(ConstElementPtr group, ConstElementPtr instance)
{
return (haveSubscription(group->stringValue(), instance->stringValue()));
}
-
}
}
diff --git a/src/lib/config/tests/fake_session.h b/src/lib/config/tests/fake_session.h
index 85e47d5..79ff174 100644
--- a/src/lib/config/tests/fake_session.h
+++ b/src/lib/config/tests/fake_session.h
@@ -87,6 +87,14 @@ public:
isc::data::ElementPtr getMessages() { return (messages_); }
isc::data::ElementPtr getMsgQueue() { return (msg_queue_); }
+ /// Throw exception on sendmsg()
+ ///
+ /// When set to true, and sendmsg() is later called, this
+ /// will throw isc::Exception
+ ///
+ /// \param value If true, enable throw. If false, disable it
+ void setThrowOnSend(bool value) { throw_on_send_ = value; }
+
private:
bool recvmsg(isc::data::ConstElementPtr& msg,
bool nonblock = true, int seq = -1);
@@ -98,6 +106,7 @@ private:
isc::data::ElementPtr subscriptions_;
isc::data::ElementPtr msg_queue_;
bool started_;
+ bool throw_on_send_;
};
} // namespace cc
} // namespace isc
diff --git a/src/lib/python/isc/config/ccsession.py b/src/lib/python/isc/config/ccsession.py
index 476ba1e..caff9f3 100644
--- a/src/lib/python/isc/config/ccsession.py
+++ b/src/lib/python/isc/config/ccsession.py
@@ -216,15 +216,18 @@ class ModuleCCSession(ConfigData):
message is just an FYI, and no response is expected. Any errors
when sending this message (for instance if the msgq session has
previously been closed) are logged, but ignored."""
+ # create_command could raise an exception as well, but except for
+ # out of memory related errors, these should all be programming
+ # failures and are not caught
msg = create_command(COMMAND_MODULE_STOPPING,
self.get_module_spec().get_full_spec())
try:
self._session.group_sendmsg(msg, "ConfigManager")
- except isc.cc.session.SessionError as se:
+ except Exception as se:
# If the session was previously closed, obvously trying to send
# a message fails. (TODO: check if session is open so we can
# error on real problems?)
- logger.error(CONFIG_SESSION_STOPPING_FAILED, str(se))
+ logger.error(CONFIG_SESSION_STOPPING_FAILED, se)
def get_socket(self):
"""Returns the socket from the command channel session. This
diff --git a/src/lib/python/isc/config/cfgmgr.py b/src/lib/python/isc/config/cfgmgr.py
index 93ab1de..f7ecce9 100644
--- a/src/lib/python/isc/config/cfgmgr.py
+++ b/src/lib/python/isc/config/cfgmgr.py
@@ -531,4 +531,6 @@ class ConfigManager:
# not ask
if msg is not None and not 'result' in msg:
answer = self.handle_msg(msg);
- self.cc.group_reply(env, answer)
+ # Only respond if there actually is something to respond with
+ if answer is not None:
+ self.cc.group_reply(env, answer)
diff --git a/src/lib/python/isc/config/tests/cfgmgr_test.py b/src/lib/python/isc/config/tests/cfgmgr_test.py
index 42291c4..c38d454 100644
--- a/src/lib/python/isc/config/tests/cfgmgr_test.py
+++ b/src/lib/python/isc/config/tests/cfgmgr_test.py
@@ -495,9 +495,14 @@ class TestConfigManager(unittest.TestCase):
def test_run(self):
self.fake_session.group_sendmsg({ "command": [ "get_commands_spec" ] }, "ConfigManager")
self.fake_session.group_sendmsg({ "command": [ "get_statistics_spec" ] }, "ConfigManager")
+ self.fake_session.group_sendmsg({ "command": [ "stopping", { "module_name": "FooModule" } ] }, "ConfigManager")
self.fake_session.group_sendmsg({ "command": [ "shutdown" ] }, "ConfigManager")
+ self.assertEqual(len(self.fake_session.message_queue), 4)
self.cm.run()
- pass
+ # All commands should have been read out by run()
+ # Three of the commands should have been responded to, so the queue
+ # should now contain three answers
+ self.assertEqual(len(self.fake_session.message_queue), 3)
if __name__ == '__main__':
diff --git a/tests/lettuce/features/bindctl_commands.feature b/tests/lettuce/features/bindctl_commands.feature
new file mode 100644
index 0000000..1c193d0
--- /dev/null
+++ b/tests/lettuce/features/bindctl_commands.feature
@@ -0,0 +1,41 @@
+Feature: control with bindctl
+ Assorted tests using bindctl for the administration of BIND 10.
+
+ Scenario: Removing modules
+ # This test runs the original example configuration, which has
+ # a number of modules. It then removes all non-essential modules,
+ # and checks whether they do disappear from the list of running
+ # modules (note that it 'misuses' the help command for this,
+ # there is a Boss command 'show_processes' but it's output is
+ # currently less standardized than 'help')
+ Given I have bind10 running with configuration example.org.config
+
+ Then remove bind10 configuration Boss/components/NOSUCHMODULE
+ last bindctl output should contain Error
+
+ bind10 module Xfrout should be running
+ bind10 module Stats should be running
+ bind10 module Zonemgr should be running
+ bind10 module Xfrin should be running
+ bind10 module Auth should be running
+ bind10 module StatsHttpd should be running
+
+ Then remove bind10 configuration Boss/components value b10-xfrout
+ last bindctl output should not contain Error
+ Then remove bind10 configuration Boss/components value b10-stats
+ last bindctl output should not contain Error
+ Then remove bind10 configuration Boss/components value b10-zonemgr
+ last bindctl output should not contain Error
+ Then remove bind10 configuration Boss/components value b10-xfrin
+ last bindctl output should not contain Error
+ Then remove bind10 configuration Boss/components value b10-auth
+ last bindctl output should not contain Error
+ Then remove bind10 configuration Boss/components value b10-stats-httpd
+ last bindctl output should not contain Error
+
+ bind10 module Xfrout should not be running
+ bind10 module Stats should not be running
+ bind10 module Zonemgr should not be running
+ bind10 module Xfrin should not be running
+ bind10 module Auth should not be running
+ bind10 module StatsHttpd should not be running
diff --git a/tests/lettuce/features/terrain/bind10_control.py b/tests/lettuce/features/terrain/bind10_control.py
index fdc419b..9210738 100644
--- a/tests/lettuce/features/terrain/bind10_control.py
+++ b/tests/lettuce/features/terrain/bind10_control.py
@@ -112,8 +112,63 @@ def have_bind10_running(step, config_file, cmdctl_port, process_name):
step.given(start_step)
step.given(wait_step)
+# function to send lines to bindctl, and store the result
+def run_bindctl(commands, cmdctl_port=47805):
+ """Run bindctl.
+ Parameters:
+ commands: a sequence of strings which will be sent.
+ cmdctl_port: a port number on which cmdctl is listening, is converted
+ to string if necessary. If not provided, or None, defaults
+ to 47805
+
+ bindctl's stdout and stderr streams are stored (as one multiline string
+ in world.last_bindctl_stdout/stderr.
+ Fails if the return code is not 0
+ """
+ if cmdctl_port is None:
+ cmdctl_port = 47805
+ args = ['bindctl', '-p', str(cmdctl_port)]
+ bindctl = subprocess.Popen(args, 1, None, subprocess.PIPE,
+ subprocess.PIPE, None)
+ for line in commands:
+ bindctl.stdin.write(line + "\n")
+ (stdout, stderr) = bindctl.communicate()
+ result = bindctl.returncode
+ world.last_bindctl_stdout = stdout
+ world.last_bindctl_stderr = stderr
+ assert result == 0, "bindctl exit code: " + str(result) +\
+ "\nstdout:\n" + str(stdout) +\
+ "stderr:\n" + str(stderr)
+
+
+ at step('last bindctl( stderr)? output should( not)? contain (\S+)')
+def check_bindctl_output(step, stderr, notv, string):
+ """Checks the stdout (or stderr) stream of the last run of bindctl,
+ fails if the given string is not found in it (or fails if 'not' was
+ set and it is found
+ Parameters:
+ stderr ('stderr'): Check stderr instead of stdout output
+ notv ('not'): reverse the check (fail if string is found)
+ string ('contain <string>') string to look for
+ """
+ if stderr is None:
+ output = world.last_bindctl_stdout
+ else:
+ output = world.last_bindctl_stderr
+ found = False
+ if string in output:
+ found = True
+ if notv is None:
+ assert found == True, "'" + string +\
+ "' was not found in bindctl output:\n" +\
+ output
+ else:
+ assert not found, "'" + string +\
+ "' was found in bindctl output:\n" +\
+ output
+
@step('set bind10 configuration (\S+) to (.*)(?: with cmdctl port (\d+))?')
-def set_config_command(step, name, value, cmdctl_port):
+def config_set_command(step, name, value, cmdctl_port):
"""
Run bindctl, set the given configuration to the given value, and commit it.
Parameters:
@@ -123,16 +178,30 @@ def set_config_command(step, name, value, cmdctl_port):
the command to. Defaults to 47805.
Fails if cmdctl does not exit with status code 0.
"""
- if cmdctl_port is None:
- cmdctl_port = '47805'
- args = ['bindctl', '-p', cmdctl_port]
- bindctl = subprocess.Popen(args, 1, None, subprocess.PIPE,
- subprocess.PIPE, None)
- bindctl.stdin.write("config set " + name + " " + value + "\n")
- bindctl.stdin.write("config commit\n")
- bindctl.stdin.write("quit\n")
- result = bindctl.wait()
- assert result == 0, "bindctl exit code: " + str(result)
+ commands = ["config set " + name + " " + value,
+ "config commit",
+ "quit"]
+ run_bindctl(commands, cmdctl_port)
+
+ at step('remove bind10 configuration (\S+)(?: value (\S+))?(?: with cmdctl port (\d+))?')
+def config_remove_command(step, name, value, cmdctl_port):
+ """
+ Run bindctl, remove the given configuration item, and commit it.
+ Parameters:
+ name ('configuration <name>'): Identifier of the configuration to remove
+ value ('value <value>'): if name is a named set, use value to identify
+ item to remove
+ cmdctl_port ('with cmdctl port <portnr>', optional): cmdctl port to send
+ the command to. Defaults to 47805.
+ Fails if cmdctl does not exit with status code 0.
+ """
+ cmd = "config remove " + name
+ if value is not None:
+ cmd = cmd + " " + value
+ commands = [cmd,
+ "config commit",
+ "quit"]
+ run_bindctl(commands, cmdctl_port)
@step('send bind10 the command (.+)(?: with cmdctl port (\d+))?')
def send_command(step, command, cmdctl_port):
@@ -144,15 +213,21 @@ def send_command(step, command, cmdctl_port):
the command to. Defaults to 47805.
Fails if cmdctl does not exit with status code 0.
"""
- if cmdctl_port is None:
- cmdctl_port = '47805'
- args = ['bindctl', '-p', cmdctl_port]
- bindctl = subprocess.Popen(args, 1, None, subprocess.PIPE,
- subprocess.PIPE, None)
- bindctl.stdin.write(command + "\n")
- bindctl.stdin.write("quit\n")
- (stdout, stderr) = bindctl.communicate()
- result = bindctl.returncode
- assert result == 0, "bindctl exit code: " + str(result) +\
- "\nstdout:\n" + str(stdout) +\
- "stderr:\n" + str(stderr)
+ commands = [command,
+ "quit"]
+ run_bindctl(commands, cmdctl_port)
+
+ at step('bind10 module (\S+) should( not)? be running')
+def module_is_running(step, name, not_str):
+ """
+ Convenience step to check if a module is running; can only work with
+ default cmdctl port; sends a 'help' command with bindctl, then
+ checks if the output contains the given name.
+ Parameters:
+ name ('module <name>'): The name of the module (case sensitive!)
+ not ('not'): Reverse the check (fail if it is running)
+ """
+ if not_str is None:
+ not_str = ""
+ step.given('send bind10 the command help')
+ step.given('last bindctl output should' + not_str + ' contain ' + name)
More information about the bind10-changes
mailing list