BIND 10 master, updated. e9798fc8931856a7eaeee37155600146d7dc7c57 Merge branch 'trac1010'
BIND 10 source code commits
bind10-changes at lists.isc.org
Tue Jun 21 08:35:47 UTC 2011
The branch, master has been updated
via e9798fc8931856a7eaeee37155600146d7dc7c57 (commit)
via a1301a0545acc48bf2f94731cb26577806e3c383 (commit)
via 4fae538655882db7c085dab798b4fb29c4a9d8f1 (commit)
via b5cfd5e541d4bbb7f13ad93392018711e19ba0e5 (commit)
via a4f1f8de765810aecff1194c74a108682e3de28e (commit)
via c4c85ce1694bd421912d1902f2d614c15bebbea1 (commit)
via f78cf6ebc22712c470da4af720915b09ae8e8ebe (commit)
via d867ca0fabdb5398d6a964aa393fadf678af2bbf (commit)
via e90d2063e0bd98767fdcd38962ad5be6f2eda68e (commit)
via 384501f85cc9e66a686a96e349241442af29a56b (commit)
via 707e700d4861b2c47235183ce6e98d985819dd2d (commit)
from 59e01319e369a7c8e4f9a326d603dee7e3924c6b (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 e9798fc8931856a7eaeee37155600146d7dc7c57
Merge: 59e01319e369a7c8e4f9a326d603dee7e3924c6b a1301a0545acc48bf2f94731cb26577806e3c383
Author: Jelte Jansen <jelte at isc.org>
Date: Tue Jun 21 10:19:46 2011 +0200
Merge branch 'trac1010'
-----------------------------------------------------------------------
Summary of changes:
src/bin/xfrout/xfrout.py.in | 2 +-
src/lib/config/ccsession.cc | 6 +-
src/lib/config/ccsession.h | 19 ++++++
src/lib/python/isc/config/ccsession.py | 32 ++++++++++-
src/lib/python/isc/config/tests/Makefile.am | 1 +
src/lib/python/isc/config/tests/ccsession_test.py | 39 ++++++++++++-
src/lib/python/isc/log/Makefile.am | 3 +
src/lib/python/isc/log/__init__.py | 11 +++-
src/lib/python/isc/log/log.cc | 64 +++++++++++++++++++++
src/lib/python/isc/log/tests/Makefile.am | 1 +
src/lib/python/isc/log/tests/log_test.py | 30 ++++++++++
11 files changed, 200 insertions(+), 8 deletions(-)
-----------------------------------------------------------------------
diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in
index 40bad85..ac22fe4 100755
--- a/src/bin/xfrout/xfrout.py.in
+++ b/src/bin/xfrout/xfrout.py.in
@@ -559,7 +559,7 @@ class XfroutServer:
#self._log = None
self._listen_sock_file = UNIX_SOCKET_FILE
self._shutdown_event = threading.Event()
- self._cc = isc.config.ModuleCCSession(SPECFILE_LOCATION, self.config_handler, self.command_handler)
+ self._cc = isc.config.ModuleCCSession(SPECFILE_LOCATION, self.config_handler, self.command_handler, None, True)
self._config_data = self._cc.get_full_config()
self._cc.start()
self._cc.add_remote_config(AUTH_SPECFILE_LOCATION);
diff --git a/src/lib/config/ccsession.cc b/src/lib/config/ccsession.cc
index 857de63..dd2be3d 100644
--- a/src/lib/config/ccsession.cc
+++ b/src/lib/config/ccsession.cc
@@ -247,7 +247,9 @@ readLoggersConf(std::vector<isc::log::LoggerSpecification>& specs,
} // end anonymous namespace
void
-my_logconfig_handler(const std::string&n, ConstElementPtr new_config, const ConfigData& config_data) {
+default_logconfig_handler(const std::string& module_name,
+ ConstElementPtr new_config,
+ const ConfigData& config_data) {
config_data.getModuleSpec().validateConfig(new_config, true);
std::vector<isc::log::LoggerSpecification> specs;
@@ -353,7 +355,7 @@ ModuleCCSession::ModuleCCSession(
// Keep track of logging settings automatically
if (handle_logging) {
- addRemoteConfig("Logging", my_logconfig_handler, false);
+ addRemoteConfig("Logging", default_logconfig_handler, false);
}
if (start_immediately) {
diff --git a/src/lib/config/ccsession.h b/src/lib/config/ccsession.h
index 53aab78..0d4b7f3 100644
--- a/src/lib/config/ccsession.h
+++ b/src/lib/config/ccsession.h
@@ -354,6 +354,25 @@ private:
ModuleSpec fetchRemoteSpec(const std::string& module, bool is_filename);
};
+/// \brief Default handler for logging config updates
+///
+/// When CCSession is initialized with handle_logging set to true,
+/// this callback will be used to update the logger when a configuration
+/// change comes in.
+///
+/// This function updates the (global) loggers by initializing a
+/// LoggerManager and passing the settings as specified in the given
+/// configuration update.
+///
+/// \param module_name The name of the module
+/// \param new_config The modified configuration values
+/// \param config_data The full config data for the (remote) logging
+/// module.
+void
+default_logconfig_handler(const std::string& module_name,
+ isc::data::ConstElementPtr new_config,
+ const ConfigData& config_data);
+
}
}
#endif // __CCSESSION_H
diff --git a/src/lib/python/isc/config/ccsession.py b/src/lib/python/isc/config/ccsession.py
index 84809f1..0869160 100644
--- a/src/lib/python/isc/config/ccsession.py
+++ b/src/lib/python/isc/config/ccsession.py
@@ -39,6 +39,10 @@
from isc.cc import Session
from isc.config.config_data import ConfigData, MultiConfigData, BIND10_CONFIG_DATA_VERSION
import isc
+from isc.util.file import path_search
+import bind10_config
+from isc.log import log_config_update
+import json
class ModuleCCSessionError(Exception): pass
@@ -116,6 +120,18 @@ def create_command(command_name, params = None):
msg = { 'command': cmd }
return msg
+def default_logconfig_handler(new_config, config_data):
+ errors = []
+
+ if config_data.get_module_spec().validate_config(False, new_config, errors):
+ isc.log.log_config_update(json.dumps(new_config),
+ json.dumps(config_data.get_module_spec().get_full_spec()))
+ else:
+ # no logging here yet, TODO: log these errors
+ print("Error in logging configuration, ignoring config update: ")
+ for err in errors:
+ print(err)
+
class ModuleCCSession(ConfigData):
"""This class maintains a connection to the command channel, as
well as configuration options for modules. The module provides
@@ -126,7 +142,7 @@ class ModuleCCSession(ConfigData):
callbacks are called when 'check_command' is called on the
ModuleCCSession"""
- def __init__(self, spec_file_name, config_handler, command_handler, cc_session = None):
+ def __init__(self, spec_file_name, config_handler, command_handler, cc_session = None, handle_logging_config = False):
"""Initialize a ModuleCCSession. This does *NOT* send the
specification and request the configuration yet. Use start()
for that once the ModuleCCSession has been initialized.
@@ -149,6 +165,11 @@ class ModuleCCSession(ConfigData):
self._session.group_subscribe(self._module_name, "*")
self._remote_module_configs = {}
+ self._remote_module_callbacks = {}
+
+ if handle_logging_config:
+ self.add_remote_config(path_search('logging.spec', bind10_config.PLUGIN_PATHS),
+ default_logconfig_handler)
def __del__(self):
# If the CC Session obejct has been closed, it returns
@@ -218,6 +239,9 @@ class ModuleCCSession(ConfigData):
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)
+ if self._remote_module_callbacks[module_name] != None:
+ self._remote_module_callbacks[module_name](new_config,
+ self._remote_module_configs[module_name])
# For other modules, we're not supposed to answer
return
@@ -260,7 +284,7 @@ class ModuleCCSession(ConfigData):
and return an answer created with create_answer()"""
self._command_handler = command_handler
- def add_remote_config(self, spec_file_name):
+ def add_remote_config(self, spec_file_name, config_update_callback = None):
"""Gives access to the configuration of a different module.
These remote module options can at this moment only be
accessed through get_remote_config_value(). This function
@@ -289,9 +313,12 @@ class ModuleCCSession(ConfigData):
if rcode == 0:
if value != None and module_spec.validate_config(False, value):
module_cfg.set_local_config(value);
+ if config_update_callback is not None:
+ config_update_callback(value, module_cfg)
# all done, add it
self._remote_module_configs[module_name] = module_cfg
+ self._remote_module_callbacks[module_name] = config_update_callback
return module_name
def remove_remote_config(self, module_name):
@@ -299,6 +326,7 @@ class ModuleCCSession(ConfigData):
if module_name in self._remote_module_configs:
self._session.group_unsubscribe(module_name)
del self._remote_module_configs[module_name]
+ del self._remote_module_callbacks[module_name]
def get_remote_config_value(self, module_name, identifier):
"""Returns the current setting for the given identifier at the
diff --git a/src/lib/python/isc/config/tests/Makefile.am b/src/lib/python/isc/config/tests/Makefile.am
index 60da781..3a1abe7 100644
--- a/src/lib/python/isc/config/tests/Makefile.am
+++ b/src/lib/python/isc/config/tests/Makefile.am
@@ -14,6 +14,7 @@ endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \
+ B10_TEST_PLUGIN_DIR=$(abs_top_srcdir)/src/bin/cfgmgr/plugins \
CONFIG_TESTDATA_PATH=$(abs_top_srcdir)/src/lib/config/tests/testdata \
CONFIG_WR_TESTDATA_PATH=$(abs_top_builddir)/src/lib/config/tests/testdata \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
diff --git a/src/lib/python/isc/config/tests/ccsession_test.py b/src/lib/python/isc/config/tests/ccsession_test.py
index a0cafd6..830cbd7 100644
--- a/src/lib/python/isc/config/tests/ccsession_test.py
+++ b/src/lib/python/isc/config/tests/ccsession_test.py
@@ -22,6 +22,7 @@ import os
from isc.config.ccsession import *
from isc.config.config_data import BIND10_CONFIG_DATA_VERSION
from unittest_fakesession import FakeModuleCCSession, WouldBlockForever
+import bind10_config
class TestHelperFunctions(unittest.TestCase):
def test_parse_answer(self):
@@ -604,7 +605,43 @@ class TestModuleCCSession(unittest.TestCase):
self.assertEqual(len(fake_session.message_queue), 1)
mccs.check_command()
self.assertEqual(len(fake_session.message_queue), 0)
-
+
+ def test_logconfig_handler(self):
+ # test whether default_logconfig_handler reacts nicely to
+ # bad data. We assume the actual logger output is tested
+ # elsewhere
+ self.assertRaises(TypeError, default_logconfig_handler);
+ self.assertRaises(TypeError, default_logconfig_handler, 1);
+
+ spec = isc.config.module_spec_from_file(
+ path_search('logging.spec', bind10_config.PLUGIN_PATHS))
+ config_data = ConfigData(spec)
+
+ self.assertRaises(TypeError, default_logconfig_handler, 1, config_data)
+
+ default_logconfig_handler({}, config_data)
+
+ # Wrong data should not raise, but simply not be accepted
+ # This would log a lot of errors, so we may want to suppress that later
+ default_logconfig_handler({ "bad_data": "indeed" }, config_data)
+ default_logconfig_handler({ "bad_data": 1}, config_data)
+ default_logconfig_handler({ "bad_data": 1123 }, config_data)
+ default_logconfig_handler({ "bad_data": True }, config_data)
+ default_logconfig_handler({ "bad_data": False }, config_data)
+ default_logconfig_handler({ "bad_data": 1.1 }, config_data)
+ default_logconfig_handler({ "bad_data": [] }, config_data)
+ default_logconfig_handler({ "bad_data": [[],[],[[1, 3, False, "foo" ]]] },
+ config_data)
+ default_logconfig_handler({ "bad_data": [ 1, 2, { "b": { "c": "d" } } ] },
+ config_data)
+
+ # Try a correct config
+ log_conf = {"loggers":
+ [{"name": "b10-xfrout", "output_options":
+ [{"output": "/tmp/bind10.log",
+ "destination": "file",
+ "flush": True}]}]}
+ default_logconfig_handler(log_conf, config_data)
class fakeData:
def decode(self):
diff --git a/src/lib/python/isc/log/Makefile.am b/src/lib/python/isc/log/Makefile.am
index 26735e7..b228caf 100644
--- a/src/lib/python/isc/log/Makefile.am
+++ b/src/lib/python/isc/log/Makefile.am
@@ -15,6 +15,9 @@ log_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
log_la_LDFLAGS = $(PYTHON_LDFLAGS)
log_la_LDFLAGS += -module
log_la_LIBADD = $(top_builddir)/src/lib/log/liblog.la
+log_la_LIBADD += $(top_builddir)/src/lib/cc/libcc.la
+log_la_LIBADD += $(top_builddir)/src/lib/config/libcfgclient.la
+log_la_LIBADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
log_la_LIBADD += $(PYTHON_LIB)
# This is not installed, it helps locate the module during tests
diff --git a/src/lib/python/isc/log/__init__.py b/src/lib/python/isc/log/__init__.py
index ad77dff..596d71c 100644
--- a/src/lib/python/isc/log/__init__.py
+++ b/src/lib/python/isc/log/__init__.py
@@ -23,7 +23,14 @@
# Should we look there? Or define something in bind10_config?
import os
+import sys
+
cwd = os.getcwd()
-pos = cwd.rfind('/src/')
-import sys; sys.path.insert(0, cwd[:pos] + '/src/lib/python/isc/log/.libs')
+base = os.path.split(cwd)[0]
+
+for base in sys.path:
+ loglibdir = os.path.join(base, 'isc/log/.libs')
+ if os.path.exists(loglibdir):
+ sys.path.append(loglibdir)
+
from log import *
diff --git a/src/lib/python/isc/log/log.cc b/src/lib/python/isc/log/log.cc
index b29c005..a671b4b 100644
--- a/src/lib/python/isc/log/log.cc
+++ b/src/lib/python/isc/log/log.cc
@@ -22,6 +22,8 @@
#include <log/logger_manager.h>
#include <log/logger.h>
+#include <config/ccsession.h>
+
#include <string>
#include <boost/bind.hpp>
@@ -49,6 +51,14 @@ namespace {
// NULL and will use the global dictionary.
MessageDictionary* testDictionary = NULL;
+// To propagate python exceptions trough our code
+// This exception is used to signal to the calling function that a
+// proper Python Exception has already been set, and the caller
+// should now return NULL.
+// Since it is only used internally, and should not pass any
+// information itself, is is not derived from std::exception
+class InternalError {};
+
PyObject*
setTestDictionary(PyObject*, PyObject* args) {
PyObject* enableO;
@@ -177,6 +187,47 @@ init(PyObject*, PyObject* args) {
Py_RETURN_NONE;
}
+PyObject*
+logConfigUpdate(PyObject*, PyObject* args) {
+ // we have no wrappers for ElementPtr and ConfigData,
+ // So we expect JSON strings and convert them.
+ // The new_config object is assumed to have been validated.
+
+ const char* new_config_json;
+ const char* mod_spec_json;
+ if (!PyArg_ParseTuple(args, "ss",
+ &new_config_json, &mod_spec_json)) {
+ return (NULL);
+ }
+
+ try {
+ isc::data::ConstElementPtr new_config =
+ isc::data::Element::fromJSON(new_config_json);
+ isc::data::ConstElementPtr mod_spec_e =
+ isc::data::Element::fromJSON(mod_spec_json);
+ isc::config::ModuleSpec mod_spec(mod_spec_e);
+ isc::config::ConfigData config_data(mod_spec);
+ isc::config::default_logconfig_handler("logging", new_config,
+ config_data);
+
+ Py_RETURN_NONE;
+ } catch (const isc::data::JSONError& je) {
+ std::string error_msg = std::string("JSON format error: ") + je.what();
+ PyErr_SetString(PyExc_TypeError, error_msg.c_str());
+ } catch (const isc::data::TypeError& de) {
+ PyErr_SetString(PyExc_TypeError, "argument 1 of log_config_update "
+ "is not a map of config data");
+ } catch (const isc::config::ModuleSpecError& mse) {
+ PyErr_SetString(PyExc_TypeError, "argument 2 of log_config_update "
+ "is not a correct module specification");
+ } catch (const std::exception& e) {
+ PyErr_SetString(PyExc_RuntimeError, e.what());
+ } catch (...) {
+ PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
+ }
+ return (NULL);
+}
+
PyMethodDef methods[] = {
{"set_test_dictionary", setTestDictionary, METH_VARARGS,
"Set or unset testing mode for message dictionary. In testing, "
@@ -198,6 +249,19 @@ PyMethodDef methods[] = {
"logging severity (one of 'DEBUG', 'INFO', 'WARN', 'ERROR' or "
"'FATAL'), a debug level (integer in the range 0-99) and a file name "
"of a dictionary with message text translations."},
+ {"log_config_update", logConfigUpdate, METH_VARARGS,
+ "Update logger settings. This method is automatically used when "
+ "ModuleCCSession is initialized with handle_logging_config set "
+ "to True. When called, the first argument is the new logging "
+ "configuration (in JSON format). The second argument is "
+ "the raw specification (as returned from "
+ "ConfigData.get_module_spec().get_full_spec(), and converted to "
+ "JSON format).\n"
+ "Raises a TypeError if either argument is not a (correct) JSON "
+ "string, or if the spec is not a correct spec.\n"
+ "If this call succeeds, the global logger settings have "
+ "been updated."
+ },
{NULL, NULL, 0, NULL}
};
diff --git a/src/lib/python/isc/log/tests/Makefile.am b/src/lib/python/isc/log/tests/Makefile.am
index 0eacbb1..b6b6b19 100644
--- a/src/lib/python/isc/log/tests/Makefile.am
+++ b/src/lib/python/isc/log/tests/Makefile.am
@@ -23,5 +23,6 @@ endif
echo Running test: $$pytest ; \
$(LIBRARY_PATH_PLACEHOLDER) \
env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/python/isc/log:$(abs_top_builddir)/src/lib/log/python/.libs \
+ B10_TEST_PLUGIN_DIR=$(abs_top_srcdir)/src/bin/cfgmgr/plugins \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
diff --git a/src/lib/python/isc/log/tests/log_test.py b/src/lib/python/isc/log/tests/log_test.py
index a463d59..4292b6c 100644
--- a/src/lib/python/isc/log/tests/log_test.py
+++ b/src/lib/python/isc/log/tests/log_test.py
@@ -16,6 +16,9 @@
# This tests it can be loaded, nothing more yet
import isc.log
import unittest
+import json
+import bind10_config
+from isc.config.ccsession import path_search
class LogDict(unittest.TestCase):
def setUp(self):
@@ -52,6 +55,33 @@ class Manager(unittest.TestCase):
# ignore errors like missing file?
isc.log.init("root", "INFO", 0, "/no/such/file");
+ def test_log_config_update(self):
+ log_spec = json.dumps(isc.config.module_spec_from_file(path_search('logging.spec', bind10_config.PLUGIN_PATHS)).get_full_spec())
+
+ self.assertRaises(TypeError, isc.log.log_config_update)
+ self.assertRaises(TypeError, isc.log.log_config_update, 1)
+ self.assertRaises(TypeError, isc.log.log_config_update, 1, 1)
+ self.assertRaises(TypeError, isc.log.log_config_update, 1, 1, 1)
+
+ self.assertRaises(TypeError, isc.log.log_config_update, 1, log_spec)
+ self.assertRaises(TypeError, isc.log.log_config_update, [], log_spec)
+ self.assertRaises(TypeError, isc.log.log_config_update, "foo", log_spec)
+ self.assertRaises(TypeError, isc.log.log_config_update, "{ '", log_spec)
+
+ # empty should pass
+ isc.log.log_config_update("{}", log_spec)
+
+ # bad spec
+ self.assertRaises(TypeError, isc.log.log_config_update, "{}", json.dumps({"foo": "bar"}))
+
+ # Try a correct one
+ log_conf = json.dumps({"loggers":
+ [{"name": "b10-xfrout", "output_options":
+ [{"output": "/tmp/bind10.log",
+ "destination": "file",
+ "flush": True}]}]})
+ isc.log.log_config_update(log_conf, log_spec)
+
class Logger(unittest.TestCase):
def tearDown(self):
isc.log.reset()
More information about the bind10-changes
mailing list