Commit 4f714bac authored by zhanglikun's avatar zhanglikun
Browse files

Merge branch 'master' into trac639

parents 6ff5cf12 1302962b
254. [bug] jinmei
b10-xfrout: failed to send notifies over IPv6 correctly.
(Trac964, git 3255c92714737bb461fb67012376788530f16e40)
253. [func] jelte
Add configuration options for logging through the virtual module
Logging.
(Trac 736, git 9fa2a95177265905408c51d13c96e752b14a0824)
252. [func] stephen
Add syslog as destination for logging.
(Trac976, git 31a30f5485859fd3df2839fc309d836e3206546e)
251. [bug]* jinmei
Make sure bindctl private files are non readable to anyone except
the owner or users in the same group. Note that if BIND 10 is run
with changing the user, this change means that the file owner or
group will have to be adjusted. Also note that this change is
only effective for a fresh install; if these files already exist,
their permissions must be adjusted by hand (if necessary).
(Trac870, git 461fc3cb6ebabc9f3fa5213749956467a14ebfd4)
250. [bug] ocean
src/lib/util/encode, in some conditions, the DecodeNormalizer's
iterator may reach the end() and when later being dereferenced
it will cause crash on some platform.
(Trac838, git 83e33ec80c0c6485d8b116b13045b3488071770f)
249. [func] jerry
xfrout: add support for TSIG verification.
(Trac816, git 3b2040e2af2f8139c1c319a2cbc429035d93f217)
......
......@@ -891,6 +891,7 @@ AC_OUTPUT([doc/version.ent
src/lib/cc/session_config.h.pre
src/lib/cc/tests/session_unittests_config.h
src/lib/log/tests/console_test.sh
src/lib/log/tests/destination_test.sh
src/lib/log/tests/local_file_test.sh
src/lib/log/tests/severity_test.sh
src/lib/log/tests/tempdir.h
......@@ -922,6 +923,7 @@ AC_OUTPUT([doc/version.ent
chmod +x src/lib/dns/tests/testdata/gen-wiredata.py
chmod +x src/lib/log/tests/local_file_test.sh
chmod +x src/lib/log/tests/console_test.sh
chmod +x src/lib/log/tests/destination_test.sh
chmod +x src/lib/log/tests/severity_test.sh
chmod +x src/lib/util/python/mkpywrapper.py
chmod +x tests/system/conf.sh
......
......@@ -13,6 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
#include <log/logger_support.h>
#include <util/unittests/run_all.h>
#include <dns/tests/unittest_util.h>
......@@ -22,6 +23,7 @@ main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
isc::log::initLogger();
return (isc::util::unittests::run_all());
}
SUBDIRS = tests
EXTRA_DIST = README tsig_keys.py tsig_keys.spec
EXTRA_DIST += logging.spec b10logging.py
config_plugindir = @prefix@/share/@PACKAGE@/config_plugins
config_plugin_DATA = tsig_keys.py tsig_keys.spec
# 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.
# This is the configuration plugin for logging options
# The name is 'b10logging' because logging.py is an existing module
#
# For a technical background, see
# http://bind10.isc.org/wiki/LoggingCppApiDesign
#
from isc.config.module_spec import module_spec_from_file
from isc.util.file import path_search
from bind10_config import PLUGIN_PATHS
spec = module_spec_from_file(path_search('logging.spec', PLUGIN_PATHS))
ALLOWED_SEVERITIES = [ 'default',
'debug',
'info',
'warn',
'error',
'fatal',
'none' ]
ALLOWED_DESTINATIONS = [ 'console',
'file',
'syslog' ]
ALLOWED_STREAMS = [ 'stdout',
'stderr' ]
def check(config):
# Check the data layout first
errors=[]
if not spec.validate_config(False, config, errors):
return ' '.join(errors)
# The 'layout' is ok, now check for specific values
if 'loggers' in config:
for logger in config['loggers']:
# name should always be present
name = logger['name']
if 'severity' in logger and\
logger['severity'].lower() not in ALLOWED_SEVERITIES:
errors.append("bad severity value for logger " + name +
": " + logger['severity'])
if 'output_options' in logger:
for output_option in logger['output_options']:
if 'destination' in output_option:
destination = output_option['destination'].lower()
if destination not in ALLOWED_DESTINATIONS:
errors.append("bad destination for logger " +
name + ": " + output_option['destination'])
else:
# if left to default, output is stdout, and
# it will not show in the updated config,
# so 1. we only need to check it if present,
# and 2. if destination is changed, so should
# output. So first check checks 'in', and the
# others 'not in' for 'output'
if destination == "console" and\
'output' in output_option and\
output_option['output'] not in ALLOWED_STREAMS:
errors.append("bad output for logger " + name +
": " + output_option['stream'] +
", must be stdout or stderr")
elif destination == "file" and\
'output' not in output_option or\
output_option['output'] == "":
errors.append("destination set to file but "
"output not set to any "
"filename for logger "
+ name)
elif destination == "syslog" and\
'output' not in output_option or\
output_option['output'] == "":
errors.append("destination set to syslog but "
"output not set to any facility"
" for logger " + name)
if errors:
return ', '.join(errors)
return None
def load():
return (spec, check)
{
"module_spec": {
"module_name": "Logging",
"module_description": "Logging options",
"config_data": [
{
"item_name": "loggers",
"item_type": "list",
"item_optional": false,
"item_default": [],
"list_item_spec": {
"item_name": "logger",
"item_type": "map",
"item_optional": false,
"item_default": {},
"map_item_spec": [
{ "item_name": "name",
"item_type": "string",
"item_optional": false,
"item_default": ""
},
{ "item_name": "severity",
"item_type": "string",
"item_optional": false,
"item_default": "INFO"
},
{ "item_name": "debuglevel",
"item_type": "integer",
"item_optional": false,
"item_default": 0
},
{ "item_name": "additive",
"item_type": "boolean",
"item_optional": false,
"item_default": false
},
{ "item_name": "output_options",
"item_type": "list",
"item_optional": false,
"item_default": [],
"list_item_spec": {
"item_name": "output_option",
"item_type": "map",
"item_optional": false,
"item_default": {},
"map_item_spec": [
{ "item_name": "destination",
"item_type": "string",
"item_optional": false,
"item_default": "console"
},
{ "item_name": "output",
"item_type": "string",
"item_optional": false,
"item_default": "stdout"
},
{ "item_name": "flush",
"item_type": "boolean",
"item_optional": false,
"item_default": false
},
{ "item_name": "maxsize",
"item_type": "integer",
"item_optional": false,
"item_default": 0
},
{ "item_name": "maxver",
"item_type": "integer",
"item_optional": false,
"item_default": 0
}
]
}
}
]
}
}
],
"commands": []
}
}
......@@ -86,7 +86,7 @@ class TSigKeysTest(unittest.TestCase):
self.assertEqual("TSIG: Invalid TSIG key string: invalid.key",
tsig_keys.check({'keys': ['invalid.key']}))
self.assertEqual(
"TSIG: attempt to decode a value not in base64 char set",
"TSIG: Unexpected end of input in BASE decoder",
tsig_keys.check({'keys': ['invalid.key:123']}))
def test_bad_format(self):
......
......@@ -40,12 +40,13 @@ b10-cmdctl: cmdctl.py
if INSTALL_CONFIGURATIONS
# TODO: permissions handled later
# Below we intentionally use ${INSTALL} -m 640 instead of $(INSTALL_DATA)
# because these file will contain sensitive information.
install-data-local:
$(mkinstalldirs) $(DESTDIR)/@sysconfdir@/@PACKAGE@
for f in $(CMDCTL_CONFIGURATIONS) ; do \
if test ! -f $(DESTDIR)$(sysconfdir)/@PACKAGE@/$$f; then \
$(INSTALL_DATA) $(srcdir)/$$f $(DESTDIR)$(sysconfdir)/@PACKAGE@/ ; \
${INSTALL} -m 640 $(srcdir)/$$f $(DESTDIR)$(sysconfdir)/@PACKAGE@/ ; \
fi ; \
done
......
......@@ -208,7 +208,8 @@ main(int argc, char* argv[]) {
cc_session = new Session(io_service.get_io_service());
config_session = new ModuleCCSession(specfile, *cc_session,
my_config_handler,
my_command_handler);
my_command_handler,
true, true);
LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_CONFIGCHAN);
// FIXME: This does not belong here, but inside Boss
......
......@@ -34,9 +34,7 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(SQLITE_LIBS)
run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libtestutils.la
run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/libdatasrc.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
......
......@@ -13,6 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
#include <log/logger_support.h>
#include <util/unittests/run_all.h>
#include <dns/tests/unittest_util.h>
......@@ -22,6 +23,7 @@ main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
isc::log::initLogger();
return (isc::util::unittests::run_all());
}
......@@ -25,7 +25,6 @@ run_unittests_SOURCES += io_fetch_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(SQLITE_LIBS)
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
......
......@@ -15,14 +15,14 @@
#include <gtest/gtest.h>
#include <util/unittests/run_all.h>
#include <log/root_logger_name.h>
#include <log/logger_manager.h>
#include <dns/tests/unittest_util.h>
int
main(int argc, char* argv[])
{
::testing::InitGoogleTest(&argc, argv); // Initialize Google test
isc::log::setRootLoggerName("unittest"); // Set a root logger name
isc::log::LoggerManager::init("unittest"); // Set a root logger name
isc::UnitTestUtil::addDataPath(TEST_DATA_DIR); // Add location of test data
return (isc::util::unittests::run_all());
......
......@@ -34,7 +34,6 @@ run_unittests_SOURCES += udp_socket_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(SQLITE_LIBS)
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
......
......@@ -14,15 +14,12 @@
#include <gtest/gtest.h>
#include <util/unittests/run_all.h>
#include <log/root_logger_name.h>
#include <dns/tests/unittest_util.h>
#include <log/logger_manager.h>
int
main(int argc, char* argv[])
{
::testing::InitGoogleTest(&argc, argv); // Initialize Google test
isc::log::setRootLoggerName("unittest"); // Set a root logger name
isc::log::LoggerManager::init("unittest"); // Set a root logger name
return (isc::util::unittests::run_all());
}
......@@ -35,6 +35,10 @@
#include <config/config_log.h>
#include <config/ccsession.h>
#include <log/logger_support.h>
#include <log/logger_specification.h>
#include <log/logger_manager.h>
using namespace std;
using isc::data::Element;
......@@ -151,6 +155,115 @@ parseCommand(ConstElementPtr& arg, ConstElementPtr command) {
}
}
namespace {
// Temporary workaround functions for missing functionality in
// getValue() (main problem described in ticket #993)
// This returns either the value set for the given relative id,
// or its default value
// (intentially defined here so this interface does not get
// included in ConfigData as it is)
ConstElementPtr getValueOrDefault(ConstElementPtr config_part,
const std::string& relative_id,
const ConfigData& config_data,
const std::string& full_id) {
if (config_part->contains(relative_id)) {
return config_part->get(relative_id);
} else {
return config_data.getDefaultValue(full_id);
}
}
// Reads a output_option subelement of a logger configuration,
// and sets the values thereing to the given OutputOption struct,
// or defaults values if they are not provided (from config_data).
void
readOutputOptionConf(isc::log::OutputOption& output_option,
ConstElementPtr output_option_el,
const ConfigData& config_data)
{
ConstElementPtr destination_el = getValueOrDefault(output_option_el,
"destination", config_data,
"loggers/output_options/destination");
output_option.destination = isc::log::getDestination(destination_el->stringValue());
ConstElementPtr output_el = getValueOrDefault(output_option_el,
"output", config_data,
"loggers/output_options/output");
if (output_option.destination == isc::log::OutputOption::DEST_CONSOLE) {
output_option.stream = isc::log::getStream(output_el->stringValue());
} else if (output_option.destination == isc::log::OutputOption::DEST_FILE) {
output_option.filename = output_el->stringValue();
} else if (output_option.destination == isc::log::OutputOption::DEST_SYSLOG) {
output_option.facility = output_el->stringValue();
}
output_option.flush = getValueOrDefault(output_option_el,
"flush", config_data,
"loggers/output_options/flush")->boolValue();
output_option.maxsize = getValueOrDefault(output_option_el,
"maxsize", config_data,
"loggers/output_options/maxsize")->intValue();
output_option.maxver = getValueOrDefault(output_option_el,
"maxver", config_data,
"loggers/output_options/maxver")->intValue();
}
// Reads a full 'loggers' configuration, and adds the loggers therein
// to the given vector, fills in blanks with defaults from config_data
void
readLoggersConf(std::vector<isc::log::LoggerSpecification>& specs,
ConstElementPtr logger,
const ConfigData& config_data)
{
const std::string lname = logger->get("name")->stringValue();
ConstElementPtr severity_el = getValueOrDefault(logger,
"severity", config_data,
"loggers/severity");
isc::log::Severity severity = isc::log::getSeverity(
severity_el->stringValue());
int dbg_level = getValueOrDefault(logger, "debuglevel",
config_data,
"loggers/debuglevel")->intValue();
bool additive = getValueOrDefault(logger, "additive", config_data,
"loggers/additive")->boolValue();
isc::log::LoggerSpecification logger_spec(
lname, severity, dbg_level, additive
);
if (logger->contains("output_options")) {
BOOST_FOREACH(ConstElementPtr output_option_el,
logger->get("output_options")->listValue()) {
// create outputoptions
isc::log::OutputOption output_option;
readOutputOptionConf(output_option,
output_option_el,
config_data);
logger_spec.addOutputOption(output_option);
}
}
specs.push_back(logger_spec);
}
} // end anonymous namespace
void
my_logconfig_handler(const std::string&n, ConstElementPtr new_config, const ConfigData& config_data) {
config_data.getModuleSpec().validateConfig(new_config, true);
std::vector<isc::log::LoggerSpecification> specs;
if (new_config->contains("loggers")) {
BOOST_FOREACH(ConstElementPtr logger,
new_config->get("loggers")->listValue()) {
readLoggersConf(specs, logger, config_data);
}
}
isc::log::LoggerManager logger_manager;
logger_manager.process(specs.begin(), specs.end());
}
ModuleSpec
ModuleCCSession::readModuleSpecification(const std::string& filename) {
std::ifstream file;
......@@ -193,7 +306,8 @@ ModuleCCSession::ModuleCCSession(
isc::data::ConstElementPtr new_config),
isc::data::ConstElementPtr(*command_handler)(
const std::string& command, isc::data::ConstElementPtr args),
bool start_immediately
bool start_immediately,
bool handle_logging
) :
started_(false),
session_(session)
......@@ -207,10 +321,8 @@ ModuleCCSession::ModuleCCSession(
session_.establish(NULL);
session_.subscribe(module_name_, "*");
//session_.subscribe("Boss", "*");
//session_.subscribe("statistics", "*");
// send the data specification
// send the data specification
ConstElementPtr spec_msg = createCommand("module_spec",
module_specification_.getFullSpec());
unsigned int seq = session_.group_sendmsg(spec_msg, "ConfigManager");
......@@ -239,9 +351,15 @@ ModuleCCSession::ModuleCCSession(
}
}
// Keep track of logging settings automatically
if (handle_logging) {
addRemoteConfig("Logging", my_logconfig_handler, false);
}
if (start_immediately) {
start();
}
}
void
......@@ -361,6 +479,11 @@ ModuleCCSession::checkCommand() {
}
} catch (const CCSessionError& re) {
LOG_ERROR(config_logger, CONFIG_CCSESSION_MSG).arg(re.what());
} catch (const std::exception& stde) {
// No matter what unexpected error happens, we do not want
// to crash because of an incoming event, so we log the
// exception and continue to run
LOG_ERROR(config_logger, CONFIG_CCSESSION_MSG_INTERNAL).arg(stde.what());
}
if (!isNull(answer)) {
session_.reply(routing, answer);
......@@ -382,9 +505,7 @@ ModuleCCSession::fetchRemoteSpec(const std::string& module, bool is_filename) {
ConstElementPtr cmd(createCommand("get_module_spec",
Element::fromJSON("{\"module_name\": \"" + module +
"\"}")));
const unsigned int seq = session_.group_sendmsg(cmd, "ConfigManager");
// Wait for the answer
unsigned int seq = session_.group_sendmsg(cmd, "ConfigManager");
ConstElementPtr env, answer;
session_.group_recvmsg(env, answer, false, seq);
int rcode;
......@@ -407,7 +528,8 @@ ModuleCCSession::fetchRemoteSpec(const std::string& module, bool is_filename) {
std::string
ModuleCCSession::addRemoteConfig(const std::string& spec_name,
void (*handler)(const std::string& module,
ConstElementPtr),
ConstElementPtr,
const ConfigData&),
bool spec_is_filename)
{
// First get the module name, specification and default config
......@@ -439,7 +561,7 @@ ModuleCCSession::addRemoteConfig(const std::string& spec_name,
remote_module_configs_[module_name] = rmod_config;
if (handler) {
remote_module_handlers_[module_name] = handler;
handler(module_name, local_config);
handler(module_name, local_config, rmod_config);
}
// Make sure we get updates in future
......@@ -487,7 +609,7 @@ ModuleCCSession::updateRemoteConfig(const std::string& module_name,
std::map<std::string, RemoteHandler>::iterator hit =
remote_module_handlers_.find(module_name);
if (hit != remote_module_handlers_.end()) {
hit->second(module_name, new_config);
hit->second(module_name, new_config, it->second);
}
}
}
......
......@@ -161,6 +161,7 @@ public:
* configuration of the local module needs to be updated.
* This must refer to a valid object of a concrete derived class of
* AbstractSession without establishing the session.
*
* Note: the design decision on who is responsible for establishing the
* session is in flux, and may change in near future.
*
......@@ -171,11 +172,14 @@ public:
* @param command_handler A callback function pointer to be called when
* a control command from a remote agent needs to be performed on the
* local module.
* @start_immediately If true (default), start listening to new commands
* @param start_immediately If true (default), start listening to new commands
* and configuration changes asynchronously at the end of the constructor;
* if false, it will be delayed until the start() method is explicitly
* called. (This is a short term workaround for an initialization trouble.
* We'll need to develop a cleaner solution, and then remove this knob)
* @param handle_logging If true, the ModuleCCSession will automatically
* take care of logging configuration through the virtual Logging config
* module.
*/
ModuleCCSession(const std::string& spec_file_name,
isc::cc::AbstractSession& session,
......@@ -184,7 +188,8 @@ public:
isc::data::ConstElementPtr(*command_handler)(
const std::string& command,
isc::data::ConstElementPtr args) = NULL,
bool start_immediately = true
bool start_immediately = true,
bool handle_logging = false
);
/// Start receiving new commands and configuration changes asynchronously.
......@@ -283,7 +288,8 @@ public:
std::string addRemoteConfig(const std::string& spec_name,
void (*handler)(const std::string& module_name,